/**
 * @file SooperADS.CorLib — Корневая библиотека для моих проектов
 * @author SooperADS
 * @version 120
 * @see `SooperADS.corelib.ts`
 */

//#region Расширение интерфейса Math
interface Math {
	/**
	 * Ограничевает значение параметра `v`
	 * @param {number} v Значение для ограничения
	 * @param {number | number[]} min Минимальный ограничитель (для ограничения будет выбранно самое маленикое значение)
	 * @param {number | number[]} max Максильный ограничитель (для ограничения будет выбранно самое большое значение)
	 * @returns {number} Ограниченно значение `v`
	 */
	clamp(
		v: number,
		min: number[] | number,
		max: number[] | number
	): number
	/**
	 * Вычисляет расстояние между точками в двухмерном пространстве
	 * @param {number} x1 X координата первой точки
	 * @param {number} x2 X координата второй точки
	 * @param {number} y1 Y координата первой точки
	 * @param {number} y2 Y координата второй точки
	 * @returns {number} Расстояние между точками
	 */
	dist2d(
		x1: number, x2: number, y1: number, y2: number
	): number
	/**
	 * Вычисляет расстояние между точками в одномерном пространстве
	 * @param {number} x1 Первая точка
	 * @param {number} x2 Вторая точка
	 * @return {number} Расстояние между точками
	 */
	dist(x1: number, x2: number): number
	/**
	 * @param {number} x Число для проверки знака
	 * @returns `true` если есть знак, мначе `false`
	 */
	hasSigned(x: number): boolean
	/**
	 * Генерирует случайное число в  заданном диапозоне  
	 *   
	 * **ПРИМЕЧАНИЕ**: Не используйте `Infinity`, `-Infinity` и `NaN` в качастве границ диапозона
	 * @param {number} min Минимальная граница диапозона
	 * @param {number} max Максимальная граница диапозона
	 * @returns Сгенерированное число
	 */
	limitRandom(min: number, max?: number): number
	/**
	 * Генерирует случайное целое число в заданном диапозоне  
	 *   
	 * **ПРИМЕЧАНИЕ**: Не используйте `Infinity`, `-Infinity` и `NaN` в качастве границ диапозона
	 * @param {number} min Минимальная граница диапозона
	 * @param {number} max Максимальная граница диапозона
	 * @returns Сгенерированное число
	 */
	intRandom(min?: number, max?: number): number
	/**
	 * Конверитрует градусы в радианы
	 * @param {number} deg Градусы
	 */
	rads(deg?: number): number
	/**
	 * Делит нацело `src` на `v`
	 * @param {number} src Делимое
	 * @param {number} v Делитель
	 * @returns {number} `Math.floor(src / v)`
	 */
	div(src: number, v: number): number
	/**
	 * Получает остаток от деления нацело `src` на `v`
	 * @param {number} src Делимое
	 * @param {number} v Делитель
	 * @returns {number} Остаток от деления
	 */
	mod(src: number, v: number): number
}

Math.clamp = function (
	v: number,
	min: number | number[],
	max: number | number[]
): number {
	const mib = (typeof (min) == "number") ? [min] : min
	const mxb = (typeof (max) == "number") ? [max] : max

	return Math.min(Math.max(
		v, ...mib
	), ...mxb)
}
Math.dist2d = function (
	x1: number,
	x2: number,
	y1: number,
	y2: number
): number {
	return Math.sqrt(
		Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)
	)
}
Math.dist = function (
	x1: number,
	x2: number,
): number {
	return Math.abs(x2 - x1)
}
Math.hasSigned = function (x: number): boolean {
	return x < 0
}
Math.limitRandom = function (
	min: number, max: number = 65536
): number {
	if (min > max) {
		let t = min;
		min = max;
		max = t;
	}

	return Math.random() * (max - min) + min;
}
Math.intRandom = function (
	min: number = -32768, max: number = 32767
): number {
	return Math.round(Math.limitRandom(min, max));
}
Math.rads = function (deg?: number): number {
	return deg ? (Math.PI / 180) * deg : 0;
}
Math.div = function (src: number, v: number): number {
	return Math.floor(src / v);
}
Math.mod = function (src: number, v: number): number {
	return src - Math.floor(src / v);
}
//#endregion

type ReqRequired<T> = {
	[P in keyof T]-?: ReqRequired<T[P]>;
};

//#region Базовые функции
/**
 * Устанавливает бит числа `v` по индексу `order` в значение `bit`
 * @param {number} v 
 * @param {number} order Индекс бита
 * @param {boolean | null | undefined} bit Значение бита.
 * @returns {number} Число с установленным битом
 */
function setNumberBit(v: number, order: number, bit: boolean | null | undefined): number {
	if (!bit) {
		bit = false;
	}
	return v | (+bit << order);
}
/**
 * Получает бит числа `v` по `order`
 * @param {number} v 
 * @param {number} order Индекс бита
 * @returns {boolean} Значение бита
 */
function getNumberBit(v: number, order: number): boolean {
	return !!(v | (1 << order));
}

/**
 * Устанавливает бит числа `v` по индексу `order` в значение `bit`
 * @param {bigint} v 
 * @param {bigint} order Индекс бита
 * @param {boolean | null | undefined} bit Значение бита.
 * @returns {bigint} Число с установленным битом
 */
function setBigintBit(v: bigint, order: bigint, bit: boolean | null | undefined): bigint {
	if (!bit) {
		bit = false;
	}
	return v | (BigInt(bit) << order);
}
/**
 * Получает бит числа `v` по `order`
 * @param {bigint} v 
 * @param {bigint} order Индекс бита
 * @returns {boolean} Значение бита
 */
function getBigintBit(v: bigint, order: bigint): boolean {
	return !!(v | (1n << order));
}

/**
 * @param {string} ch Искомая строка (для проверки)
 * @param {...string} chl Список соответствий
 * @returns {boolean} `true` если искомя строка равна хотябы одной строке из списка соответствий, иначе `false`
 */
function charIs<T extends string = string>(ch: T | undefined, ...chl: T[]): boolean {
	if (ch === undefined) {
		return chl.length === 0;
	}

	for (let i = 0; i < chl.length; i++) {
		if (chl[i] == ch) {
			return true;
		}
	}

	return false;
}

/**
 * Создаёт строку из заданного фрагмента
 * @param {string} ch Фрагмент для создания строки
 * @param {number} c Количество символов
 * @param {string} base Базовая строка
 * @returns {string} Сгенерированная строка
 */
function stringByChar(ch: string, c: number, base: string = ""): string {
	for (let i = 0; i < c; i++) {
		base += ch;
	}
	return base;
}

/**
 * @param {number} v 
 * @returns {number} Целая версия числа `v`
 */
function numberToUInt(v: number): number {
	return Math.max(numberToInt(v), 0);
}
/**
 * @param {number} v 
 * @returns {number} Целая абсолютная версия числа `v`
 */
function numberToInt(v: number): number {
	return ~~v;
}
/**
 * @param {number} v 
 * @returns {number} Абсолютная версия числа `v`
 */
function numberToUnsigned(v: number): number {
	return Math.max(v, 0);
}

/**
 * Создаёт задержку
 * 
 * @async
 * @param {number} ms Задержка в миллисикундах
 * @returns {Promise<void>} Промис, который выполнется через заданное время
 */
function wait(ms: number): Promise<void> {
	return new Promise<void>((returnFunction) => {
		setTimeout(() => {
			returnFunction();
		}, ms)
	})
}
/**
 * Проверяет, существует лми значение параметра `value`
 * @param {any} value Значение, для проверки
 * @returns {boolean} `true` если `value` не равно `null` или `undefined`
 */
function exist(value: any): boolean {
	return value !== null || value !== undefined;
}
//#endregion

//#region Декораторы
/**
 * Декоратор класса  
 * Устанавлевает пользовательский прототип, переданный параметром `proto`
 * 
 * @function $customePrototype
 * @param {object | null} proto Прототип
 */
const $cunstomPrototype = function $cunstomPrototypeFactory(proto: object | null): ClassDecorator {
	return function $cunstomPrototypeDecorator(f) {
		Object.setPrototypeOf(f.prototype, proto)
		return f;
	}
};
/**
 * Декоратор класса  
 * Устанавлевает прототип равный `null`
 * 
 * @function $noPrototype
 */
const $noPrototype = $cunstomPrototype(null);
/**
 * Декоратор класса  
 * Запрещает изменения и модификацию свойства `constructor` у своих экземпляров
 */
const $safedConstructor: ClassDecorator = function $safedConstructorDecorator(f) {
	Object.defineProperty(f.prototype, "constructor", {
		configurable: false,
		enumerable: false,
		writable: false,
		value: f
	});
};
//#endregion

/**
 * @namespace
 */
namespace SooperADS {
	/**
	 * Создаёт уникальный символ с задвнным именем `name` для заданного класса
	 * 
	 * @param {Function | null} ownctor Конструктор класса, к которому должен 
	 * принадлежать символ. Если равен `null`, символ не принадлежит никакому классу
	 * @param {? string} name Имя символа
	 * @deprecated Вместо этого используйте *Slot API*
	 * @returns {unique symbol} Сгенерированный символ
	 */
	export function Slot(ownctor: Function | null, name?: string | number | undefined): symbol {
		const pref = ownctor !== null
			? ownctor.name + (
				name !== undefined
					? '.' : ""
			)
			: '#' + (
				name !== undefined
					? ':' : ""
			);

		if (name === undefined) {
			return Symbol(pref);
		}
		return Symbol(`${pref}${name}`);
	}

	namespace Private {
		export declare const TokenStorage: unique symbol;
		export declare const Token: unique symbol;
		export declare const GetEvent: unique symbol;
		export declare const DispatchEvent: unique symbol;
		export declare const UpdateParent: unique symbol;
		export declare const TempPreventCallbacks: unique symbol;

		export declare const BUBBLED_EVENT_TARGET_OBJECT: SlotToken;
		export declare const EVENT_TARGET_BASE_OBJECT: SlotToken;
		export declare const READONLY_ARRAY_OBJECT: SlotToken;
		export declare const BUBBLED_EVENT_OBJECT: SlotToken;
		export declare const BASE_WRITABLE_OBJECT: SlotToken;
		export declare const POSITION_2D_OBJECT: SlotToken;
		export declare const CUSTOM_ELEM_OBJECT: SlotToken;
		export declare const BIT_COLOR_OBJECT: SlotToken;
		export declare const COMPONENT_OBJECT: SlotToken;
		export declare const REGISTRY_OBJECT: SlotToken;
		export declare const EVENT_OBJECT: SlotToken;
		export declare const BASE_OBJECT: SlotToken;
	}

	//#region Slot API
	/**
	 * Модиффикатор наследования ссылок на слот
	 * 
	 * ### Возможные значения
	 * 
	 * `"never"` — Символ не наследуется потомками  
	 * `"always"` — Символ всегда наследуется потомками  
	 * `"overridable"` — Символ наследуется потомками, однако каждый из потомков может изменить модификатор наследования символа
	 */
	export type BaseSlotTokenInherit = "never" | "overridable" | "always"

	/**
	 * Объект управления символами класса  
	 * Неотъемлемая часть *Slot API*
	 */
	export interface SlotToken {
		/**
		 * Класс (точнее конструктор обхектов данного класса), к которому привязан **SlotToken**: предок
		 */
		readonly owner: NewableFunction;
		/**
		 * Имя владельца *слот-токена*
		 */
		readonly ownerName: string;
		/**
		 * Длинна списка определяемых данным токеном *слотов*
		 */
		readonly length: number;
		/**
		 * Список всех доступных *слотов*
		 * 
		 * >**ПРИМЕЧАНИЕ**: В данный список входнят все определённые данным токеном слоты
		 * а также все видимые (имеющие модиффикатор наследования `"always"` или `"overridable"` у токена предка) слоты.
		 */
		readonly names: string[];
		/**
		 * Список определяемых данным токеном *слотов*
		 */
		readonly definedNames: string[];

		[Private.TokenStorage]: {
			[v: string]: {
				readonly inherit: BaseSlotTokenInherit;
				readonly value?: symbol;
			}
		}

		/**
		 * Получает или при необходимости создаёт ссылку (символ) на *слот* с заданным именем
		 * @param {string} name Имя *слота*
		 * @returns {symbol} Ссылка (символ) на *слот*
		 */
		at(name: string): symbol;
		/**
		 * Определяет новый *слот* для текущего класса
		 * @param {string} name Имя нового *слота*
		 * @param {BaseSlotTokenInherit} inherit Модификатор наследования *слота*
		 * @returns {unique symbol | undefined} Вернёт ссылку (символ) на *слот*. 
		 * В случае если слот с данным именем уже объявлен и существует, или если
		 * уаказанно неверное имя модиффикатора наследования, функция вернёт `undefined`
		 */
		define(name: string, inherit: BaseSlotTokenInherit): symbol | undefined;
		/**
		 * Получает ссылку (символ) на *слот* с заданным именем
		 * @param {string} name Имя *слота*
		 * @returns {symbol | undefined} Ссылка на *слот*. `undefined`, если *слот* не доступен или не определён
		 */
		get(name: string): symbol | undefined;
		/**
		 * Получает модиффикатор наследования *слота* с заданным именем
		 * @param {string} name Имя *слота*
		 * @returns {BaseSlotTokenInherit | undefined} Модиффикатор наследования *слота*. `undefined`, если *слот* не определён
		 */
		getInherit(name: string): BaseSlotTokenInherit | undefined;
		/**
		 * Проверяет доступность *слота* с заданным именем
		 * @param {string} name Имя *слота*
		 * @returns {boolean} `true`, если *слот* доступен, иначе `false`
		 */
		has(name: string): boolean;
		/**
		 * Проверяет перезапись модификатора наследования *слота* с заданным именем
		 * @param {string} name Имя *слота*
		 * @returns {boolean} `true`, если *слот* определён или перезаписан его модиффикатор наследования 
		 * данным *Slot Token-ом*, иначе `false`
		 */
		hasOverride(name: string): boolean;
		/**
		 * Изменяет модификатор наследования у определённого потомкам (или потомками)  и доступного слота
		 * @param {string} name Имя *слота*
		 * @param {BaseSlotTokenInherit} newinherit Новый модификатор наследования *слота*
		 * @returns {boolean} `true` если слот для перезаписи не определён нанным токеном и 
		 * его модиффикатор наследования имеет значение, раное `"overridable"`. Иначе `false`
		 */
		override(name: string, newinherit: BaseSlotTokenInherit): boolean;

		/**
		 * Конвертирует объект в строку
		 * @return {string} Строковое представление объекта
		 */
		toString(): string;
	}
	export interface SlotTokenConstructor {
		(constructor: NewableFunction): SlotToken;
	}

	export const SlotToken: SlotTokenConstructor = function token(constructor: NewableFunction): SlotToken {
		if (new.target) {
			throw new TypeError("function `SooperADS.BaseSlotToken` is not a constructor");
		}

		const r = Object.create(SlotToken.prototype) as SlotToken;

		if ((constructor as any)[Private.TokenStorage]) {
			throw new TypeError("Нельзя создать более 1 токена слота для одиного класса");
		}

		Object.defineProperty(constructor, Private.Token, {
			configurable: false,
			enumerable: false,
			writable: false,
			value: r
		});
		Object.defineProperty(r, Private.TokenStorage, {
			configurable: false,
			enumerable: false,
			writable: false,
			value: Object.create(null)
		});
		Object.defineProperty(r, "owner", {
			configurable: false,
			enumerable: false,
			writable: false,
			value: constructor
		});
		Object.defineProperty(r, "ownerName", {
			configurable: false,
			enumerable: true,
			writable: false,
			value: constructor.name
		});
		Object.preventExtensions(r);

		return r;
	}
	Object.defineProperty(SlotToken, "prototype", {
		configurable: false,
		enumerable: false,
		writable: false,
		value: Object.create(null, {
			"constructor": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: SlotToken
			},
			"define": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function define(
					this: SlotToken, name: string, inherit: BaseSlotTokenInherit
				): symbol | undefined {
					if (!charIs(inherit, "always", "never", "overridable")) {
						return undefined;
					}

					if (this.has(name)) {
						return undefined;
					}

					const r = Slot(this.owner, name);
					this[Private.TokenStorage][name] = Object.create(null, {
						inherit: {
							configurable: false,
							enumerable: false,
							writable: false,
							value: inherit
						},
						value: {
							configurable: false,
							enumerable: false,
							writable: false,
							value: r
						},
					});
					Object.preventExtensions(this[Private.TokenStorage][name]);
					return r;
				}
			},
			"get": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function getKey(
					this: SlotToken, name: string
				): symbol | undefined {
					let own: NewableFunction | null = this.owner;
					let towner = true;

					while (own) {
						//@ts-ignore
						let t: SlotToken = own[Private.Token];

						if (t) {
							const c = t[Private.TokenStorage][name];
							if (c?.value && (towner || c.inherit !== "never")) {
								return c.value;
							}
						}
						own = Object.getPrototypeOf(own);
						towner = false;
					}

					return undefined;
				}
			},
			"getInherit": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function getInherit(
					this: SlotToken, name: string
				): BaseSlotTokenInherit | undefined {
					let own: NewableFunction | null = this.owner;

					while (own) {
						//@ts-ignore
						let t: SlotToken = own[Private.Token];

						if (t) {
							const c = t[Private.TokenStorage][name];
							if (c) {
								return c.inherit;
							}
						}
						own = Object.getPrototypeOf(own);
					}

					return undefined;
				}
			},
			"has": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function has(
					this: SlotToken, name: string
				): boolean {
					let own: NewableFunction | null = this.owner;
					let to = true;

					while (own) {
						//@ts-ignore
						let t: SlotToken = own[Private.Token];

						if (t) {
							const c = t[Private.TokenStorage][name];
							if (c) {
								if (c.inherit === "never" && !to) {
									return false;
								}
								return true;
							}
						}
						own = Object.getPrototypeOf(own);
						to = false;
					}

					return false;
				}
			},
			"hasOverride": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function hasOverride(
					this: SlotToken, name: string
				): boolean {
					const c = this[Private.TokenStorage][name];
					if (c) {
						return true;
					}
					return false;
				}
			},
			"override": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function override(
					this: SlotToken, name: string, newinherit: BaseSlotTokenInherit
				): boolean {
					if (!charIs(newinherit, "always", "never", "overridable")) {
						return false;
					}

					if (this.hasOverride(name)) {
						return false;
					}

					let i = this.getInherit(name)
					if (i !== "overridable") {
						return false;
					}

					if (!i) {
						this.define(name, newinherit)
						return true;
					}

					this[Private.TokenStorage][name] = Object.create(null, {
						inherit: {
							configurable: false,
							enumerable: false,
							writable: false,
							value: newinherit
						}
					});
					Object.preventExtensions(this[Private.TokenStorage][name]);
					return true;
				}
			},
			"at": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function at(this: SlotToken, name: string): symbol {
					let r = this.get(name);

					if (!r) {
						return this.define(name, "overridable")!
					}
					return r;
				}
			},

			"toString": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function toString(this: SlotToken): string {
					return `#[class ${this.ownerName}]`;
				}
			},

			"length": {
				configurable: false,
				enumerable: false,
				get: function (this: SlotToken): number {
					return this.definedNames.length;
				}
			},
			"names": {
				configurable: false,
				enumerable: false,
				get: function (this: SlotToken): string[] {
					const r: string[] = [];
					let t: null | SlotToken = this;

					while (t) {
						r.push(...t.definedNames);
						t = Object.getPrototypeOf(t.owner)?.[Private.Token] ?? null;
					}

					return r;
				}
			},
			"definedNames": {
				configurable: false,
				enumerable: false,
				get: function (this: SlotToken): string[] {
					return Object.keys(this[Private.TokenStorage]);
				}
			},
		})
	});

	Object.defineProperty(Private, "TokenStorage", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: Slot(SlotToken, "SlotControler")
	});
	Object.defineProperty(Private, "Token", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: Slot(null, "Token")
	});
	//#endregion

	//#region Базовые объекты
	/**
	 * Базовый класс для моих объектов (не всех)
	 * 
	 * ### Слоты
	 * 
	 * | Имя                  | Наследование  | Назначение                                        |
	 * |----------------------|---------------|---------------------------------------------------|
	 * | `PrototypeChangeble` | `overridable` | Определяет, возможно ли изменить прототип объекта |
	 * | `Tag`                | `always`      | Определяет тег объекта. Опционален                |
	 */
	export interface Base {
		/**
		 * Текущий прототип объета
		 */
		prototype: object | null;

		/**
		 * Определяет, может ли текущий прототип объекта быть изменён
		 */
		readonly prototypeWritable: boolean;
		/**
		 * Конструктор объекта
		 */
		readonly constructor: Function;

		/**
		 * Сравнивает текущий объект с чем-либо
		 * @param {unknown} object Значение для сравнения
		 * @returns `true`, если объекты примерно равны, наче `false`
		 */
		isEqual(object: unknown): boolean;

		/**
		 * Запрещает изменение текущено прототипа объекта
		 */
		prototypeFreeze(): void;
		/**
		 * Конвертирует объект в строку
		 */
		toString(): string;
	}
	export interface BaseConstructor {
		new(): Base

		readonly [Private.Token]: SlotToken;
		readonly prototype: Base;

		/**
		 * @returns "Публичеый" управляющий `Slot Token` данного класса
		 */
		publicToken(): SlotToken;

		/**
		 * Позволяет добавить свойство в `this.prototype`
		 * @param {PropertyKey} key Ключ свойства
		 * @param {PropertyDescriptor} desc Дескриптор свойства
		 */
		defineProperty(key: PropertyKey, desc: PropertyDescriptor): void;
		/**
		 * Позволяет добавить несколько свойств в `this.prototype`
		 * @param {PropertyDescriptorMap} map Карта дискрипторов свойств
		 */
		defineStructure(map: PropertyDescriptorMap): void;
	}

	export const Base: BaseConstructor = function Base(this: Base): Base {
		_INIT_SLOT(this, Private.BASE_OBJECT, "PrototypeChangeble", true, false);

		return this;
	} as unknown as BaseConstructor
	Object.defineProperty(Base, "defineProperty", {
		configurable: false,
		enumerable: false,
		writable: false,
		value: function defineProperty(this: BaseConstructor, key: PropertyKey, desc: PropertyDescriptor): void {
			if (desc.configurable === undefined) {
				desc.configurable = false;
			}
			if (desc.enumerable === undefined) {
				desc.enumerable = true;
			}

			Object.defineProperty(this.prototype, key, desc)
		}
	});
	Object.defineProperty(Base, "defineStructure", {
		configurable: false,
		enumerable: false,
		writable: false,
		value: function defineStructure(this: BaseConstructor, map: PropertyDescriptorMap): void {
			for (const key in map) {
				let desc = map[key]!;
				this.defineProperty(key, desc);
			}
		}
	});
	Object.defineProperty(Base, "publicToken", {
		configurable: false,
		enumerable: false,
		writable: false,
		value: function publicToken(this: BaseConstructor): SlotToken {
			return Base[Private.Token];
		}
	});
	Object.defineProperty(Base, "prototype", {
		configurable: false,
		enumerable: false,
		writable: false,
		value: Object.create(null, {
			"prototype": {
				configurable: false,
				enumerable: false,
				get: function prototype(this: Base): object | null {
					return Object.getPrototypeOf(this);
				},
				set: function prototype(this: Base, v: object | null): void {
					if (this.prototypeWritable) {
						console.warn("Смена прототипа объекта, унаследованного от", Base, ':\n', this);
						Object.setPrototypeOf(this, v);
					}
				},
			},
			"prototypeWritable": {
				configurable: false,
				enumerable: false,
				get: function prototypeWritable(this: Base): boolean {
					return Boolean(_GET_SLOT(this, Private.BASE_OBJECT, "PrototypeChangeble"));
				},
			},
			"prototypeFreeze": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function prototypeFreeze(this: Base): void {
					_INIT_SLOT(this, Private.BASE_OBJECT, "PrototypeChangeble", false, true);
				}
			},
			"constructor": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: Base
			},
			"toString": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function toString(this: Base): string {
					return Object.prototype.toString.call(this);
				}
			},
			"isEqual": {
				configurable: false,
				enumerable: false,
				writable: false,
				value: function is(this: Base, v: unknown): boolean {
					return this === v;
				}
			},
			[Symbol.toStringTag]: {
				configurable: false,
				enumerable: false,
				get: function (this: Base): string {
					return _GET_SLOT(this, Private.BASE_OBJECT, "Tag")
						?? (this?.constructor as Function).name ?? "unkown";
				}
			}
		})
	});

	/**
	 * Базовый изменяемый обхект объект
	 * 
	 * ### Слоты
	 * 
	 * | Имя        | Наследование | Назначение                                        |
	 * |------------|--------------|---------------------------------------------------|
	 * | `Readonly` | `never`      | Определяет, возможно ли изменить свойства объекта |
	 */
	@$safedConstructor
	export class BaseWritable extends Base {
		constructor() {
			super();
			_INIT_SLOT(this, Private.BASE_WRITABLE_OBJECT, "Readonly", false, false);
		}

		/**
		 * Может ли объект быть изменён
		 */
		declare readonly writtable: boolean;

		/**
		 * Запрещает изменять объект
		 */
		preventChange(): void {
			_INIT_SLOT(this, Private.BASE_WRITABLE_OBJECT, "Readonly", true, true);
		}
	}

	BaseWritable.defineProperty("writtable", {
		get: function writtable(this: BaseWritable): boolean {
			return !_GET_SLOT(this, Private.BASE_WRITABLE_OBJECT, "Readonly") ?? false;
		}
	});

	/**
	 * Итератор объекта типа `SooperADS.ReadonlyArrayCollection`
	 */
	export interface ReadonlyArrayCollectionIterator<T> {
		/**
		 * Индекс
		 */
		index: number;
		/**
		 * Значение по индексу
		 */
		value: T | undefined;
	}

	/**
	 * Коллекци элементов, недоступная для изменения
	 * 
	 * ### Слоты
	 * 
	 * | Имя          | Наследование  | Назначение                    |
	 * |--------------|---------------|-------------------------------|
	 * | `OwnerArray` | `overridable` | ССылка на оригинальный массив |
	 */
	export class ReadonlyArrayCollection<T> extends Base {
		/**
		 * Создаёт коллекцию элементов, нодоступную для изменения
		 * @param {T[]} array Ссылка на оригинальную коллекцию
		 * 
		 * > **ВАЖНО**: Так как данный объект неизменяем, то для получения элементов 
		 * будет использована оригинальная коллекция (параметр `array`)
		 */
		constructor(array: T[]) {
			super();

			_INIT_SLOT(this, Private.READONLY_ARRAY_OBJECT, "OwnerArray", array, true);
			this.prototypeFreeze();
		}

		/**
		 * Получает элемент оригинального массива по заданному индексу
		 * @param {number} index Индекс
		 * @returns {T | undefined} `undefined` если элемент в
		 * оригинальном массиве по заданному индексу равен `undefined` или
		 * элеметна не существует
		 */
		item(index: number): T | undefined {
			return _GET_SLOT<T[]>(this, Private.READONLY_ARRAY_OBJECT, "OwnerArray")![index];
		}
		/**
		 * Получает элемент с конца оригинального массива по заданному индексу
		 * @param {number} index Индекс с конца
		 * @returns {T | undefined} `undefined` если элемент в
		 * оригинальном массиве по заданному индексу равен `undefined` или
		 * элеметна не существует
		 */
		lastItem(index: number): T | undefined {
			return _GET_SLOT<T[]>(this, Private.READONLY_ARRAY_OBJECT, "OwnerArray")![this.length - index - 1];
		}
		/**
		 * Проверяет наличие заданного эленмента в массиве
		 * @param {T} item Элемент
		 * @returns {boolean} `true` если элемент присутствует в массиве, иначе `false`
		 */
		contains(item: T): boolean {
			return this.indexOf(item) !== null;
		}

		/**
		 * Ищет индекс элемента в оригинальном массиве
		 * @param {T} item Элемен для поиска
		 * @param {?boolean} checkEqual Если равен `true` и значение 
		 * параметра `item` наследуется от `SooperADS.Base` или его потомков, 
		 * то быдет выполнятся дополнительная проверка на равенство с помощью 
		 * функции `SooperADS.Base.isEqual`
		 * @returns {null | number} `null` если элемента не в массиве, 
		 * иначе вернёт индекс элемента в оригинальном массиве
		 */
		indexOf(item: T, checkEqual: boolean = true): null | number {
			const array = _GET_SLOT<T[]>(this, Private.READONLY_ARRAY_OBJECT, "OwnerArray")!;
			for (let i = 0; i < array.length; i++) {
				const e = array[i];

				if (e === item) {
					return i;
				}
				else if (e instanceof Base && checkEqual) {
					if (e.isEqual(item)) {
						return i
					}
				}
			}
			return null;
		}
		/**
		 * Ищет индекс элемента с конца в оригинальном массиве
		 * @param {T} item Элемен для поиска
		 * @param {?boolean} checkEqual Если равен `true` и значение 
		 * параметра `item` наследуется от `SooperADS.Base` или его потомков, 
		 * то быдет выполнятся дополнительная проверка на равенство с помощью 
		 * функции `SooperADS.Base.isEqual`
		 * @returns {null | number} `null` если элемента не в массиве, 
		 * иначе вернёт индекс элемента в оригинальном массиве
		 */
		lastIndexOf(item: T, checkEqual: boolean = true): null | number {
			const array = _GET_SLOT<T[]>(this, Private.READONLY_ARRAY_OBJECT, "OwnerArray")!;
			for (let i = array.length - 1; i >= 0; i--) {
				const e = array[i];

				if (e === item) {
					return i;
				}
				else if (e instanceof Base && checkEqual) {
					if (e.isEqual(item)) {
						return i
					}
				}
			}
			return null;
		}

		/**
		 * @see {@link Array.forEach}
		 */
		forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void {
			_GET_SLOT<T[]>(this, Private.READONLY_ARRAY_OBJECT, "OwnerArray")!.forEach(callbackfn, thisArg);
		}

		[Symbol.iterator](): Iterator<ReadonlyArrayCollectionIterator<T>> {
			let index = -1;
			const array = _GET_SLOT<T[]>(this, Private.READONLY_ARRAY_OBJECT, "OwnerArray")!;
			return {
				next: function itterate() {
					index++;
					if (array.length > index) {
						return {
							done: false,
							value: {
								index: index,
								value: array[index]
							}
						};
					}
					else {
						return {
							done: true,
							value: undefined
						};
					}
				}
			}
		}

		/**
		 * Получает длинну оригенального масива
		 */
		declare readonly length: number;
	}
	ReadonlyArrayCollection.defineProperty("length", {
		get: function getLength<T>(this: ReadonlyArrayCollection<T>): number {
			return _GET_SLOT<T[]>(this, Private.READONLY_ARRAY_OBJECT, "OwnerArray")!.length;
		}
	})
	//#endregion

	//#region Расширение Slot API
	/**
	 * Строгий режим для функции прямой работы со *слотами* 
	 * В строгом режиме нельзя использовать токен другого не родительского класса.
	 * @type {boolean}
	 */
	export let _SLOT_STRICKT_MODE: boolean = true;

	/**
	 * #### Функция работы со *слотами*
	 * 
	 * Получает знчение внутри *слота* у заданного объекта 
	 * @param {Base} object Объект
	 * @param {SlotToken} t Токен объекта
	 * @param {string} slot Имя слота
	 * @returns {T} Значение
	 */
	export function _GET_SLOT<T>(object: Base, t: SlotToken, slot: string): T | undefined {
		if (!t || !(t instanceof SlotToken)) {
			return undefined;
		}

		if (!(object instanceof t.owner) && _SLOT_STRICKT_MODE) {
			console.warn("Токен", t, "не соответствует классу объекту", object);
			return undefined;
		}

		let key = t.get(slot);
		if (key) {
			return (object as any)[key];
		}

		if (_SLOT_STRICKT_MODE) {
			console.warn("Токен", t.toString(), "публично не определяет слот", slot);
		}
		return undefined;
	}
	/**
	 * #### Функция работы со *слотами*
	 * 
	 * Проверяет, объявлен ли *слот* у заданного объекта
	 * @param {Base} object Объект
	 * @param {SlotToken} t Токен объекта
	 * @param {string} slot Имя *слота*
	 * @returns {boolean} `true` если объявлен, иначе `false`
	 */
	export function _HAS_SLOT(object: Base, t: SlotToken, slot: string): boolean {
		if (!t || !(t instanceof SlotToken)) {
			return false;
		}

		if (!(object instanceof t.owner) && _SLOT_STRICKT_MODE) {
			console.warn("Токен", t, "не соответствует классу объекту", object);
			return false;
		}

		if (t.has(slot)) {
			return Object.hasOwn(object, t.get(slot)!)
		}

		if (_SLOT_STRICKT_MODE) {
			console.warn("Токен", t.toString(), "публично не определяет слот", slot);
		}
		return false;
	}
	/**
	 * #### Функция работы со *слотами*
	 * 
	 * Устанавливает знчение внутри *слота* у заданного объекта 
	 * @param {Base} object Объект
	 * @param {SlotToken} t Токен объекта
	 * @param {string} slot Имя *слота*
	 * @param {T} v Значение
	 */
	export function _SET_SLOT<T>(object: Base, t: SlotToken, slot: string, v: T): void {
		if (!t || !(t instanceof SlotToken)) {
			if (_SLOT_STRICKT_MODE) {
				console.warn("Токен ", t, "не определяет слот ", slot);
			}
			return;
		}

		if (!(object instanceof t.owner) && _SLOT_STRICKT_MODE) {
			console.warn("Токен", t, "не соответствует классу объекту", object);
			return;
		}

		let key = t.get(slot);
		if (key) {
			(object as any)[key] = v;
		}
		else if (_SLOT_STRICKT_MODE) {
			console.warn("Токен", t.toString(), "публично не определяет слот", slot);
		}
	}
	/**
	 * #### Функция работы со *слотами*
	 * 
	 * Инициализирует *слот* у заданного объекта 
	 * @param {Base} object Объект
	 * @param {SlotToken} t Токен объекта
	 * @param {string} slot Имя *слота*
	 * @param {T} dval Значение *слота*
	 * @param {boolean} readonly Неизменяем ли слот. Неизменяемые слоты нельзя удалить
	 * @return {boolean} `true` если всё ок, иначе `false`
	 */
	export function _INIT_SLOT<T>(object: Base, t: SlotToken, slot: string, dval: T, readonly: boolean = false): boolean {
		if (!t || !(t instanceof SlotToken)) {
			return false;
		}

		if (!(object instanceof t.owner) && _SLOT_STRICKT_MODE) {
			console.warn("Токен", t, "не соответствует классу объекту", object);
			return false;
		}

		let key = t.get(slot);
		if (key) {
			return Reflect.defineProperty(object, key, {
				configurable: !readonly,
				enumerable: false,
				writable: !readonly,
				value: dval
			});
		}

		if (_SLOT_STRICKT_MODE) {
			console.warn("Токен", t.toString(), "публично не определяет слот", slot);
		}
		return false;
	}
	/**
	 * #### Функция работы со *слотами*
	 * 
	 * Удаляет *слот* у заданного объекта 
	 * @param {Base} object Объект
	 * @param {SlotToken} t Токен объекта.
	 * @param {string} slot Имя слота
	 * @returns {Boolean} `true` если всё ок, иначе `false`
	 */
	export function _REMOVE_SLOT(object: Base, t: SlotToken, slot: string): boolean {
		if (!t || !(t instanceof SlotToken)) {
			if (_SLOT_STRICKT_MODE) {
				console.warn("Токен ", t, "не определяет слот ", slot);
			}
			return false;
		}

		if (!(object instanceof t.owner) && _SLOT_STRICKT_MODE) {
			console.warn("Токен", t, "не соответствует классу объекту", object);
			return false;
		}

		let key = t.get(slot);
		if (key) {
			return Reflect.deleteProperty(object, key);
		}

		if (_SLOT_STRICKT_MODE) {
			console.warn("Токен", t.toString(), "публично не определяет слот", slot);
		}
		return false;
	}

	/**
	 * Создаёт объект `SooperADS.SlotToken` для заданного класса, с заданным именем в заданном объекте по заданному ключу
	 * @param {NewableFunction} tokenOwner Класс-владелец токена
	 * @param {string} name Ключ, по которому в заданном объекте `namespace` будет объявлен токен
	 * @param {object} namespace Объект, где будет определён токен
	 * @returns {SlotToken} `SlotToken` заданного класса
	 */
	export function _DEFINE_SLOT_TOKEN(tokenOwner: NewableFunction, name: string, namespace: object): SlotToken {
		const token = SlotToken(tokenOwner);
		Object.defineProperty(namespace, name, {
			configurable: false,
			enumerable: true,
			writable: false,
			value: token
		});
		return token;
	}
	//#endregion

	//#region Реализация событий
	//#region Event
	/**
	 * Состояние обработки события
	 * 
	 * ### Возможные значения
	 * 
	 * `"init"` — Событие инициализированно, но не обработанно  
	 * `"release"` — Событие обработанно  
	 * `"work"` — Событие в обработке  
	 * `"break"` — Событие инициализированно, но его обработка была прервана  
	 * `"unknown"` — Состояние неизвестно. **ЗАРЕЗЕРВИРОВАННО**
	 * `"error"` — Ошибка при создании объекта события
	 */
	export type EventState = "init" | "release" | "work" | "unknown" | "break" | "error";

	/** 
	 * Полное имя события. Может содержать **ТОЛКО** символы англиского алфавита 
	 * в произвольном регистре, цифры и следующие спец. символы: `.`, `+`, `/`, `^` и `:`.
	 * 
	 * ### Немного о значении символов
	 * 
	 * Специальные символы являются *операторами именования* события. Они образуют 
	 * *дополнительные имена события*. Каждое такое дополнительное имя используется для
	 * обработки события. Именно они учитываются при обработке *селектора событий* 
	 * 
	 * @example ```Typescript
	 * // Переменная объявдена но не инициализированна. Для удобства представте что 
	 * // здесь создаётся какой-нибудь произвольный от `SooperADS.EventTarget` объект
	 * declare const target: SooperADS.EventTarget;
	 * const event = new SooperADS.Event("a+b", target);
	 * 
	 * target.subscribe("a", () => console.log("A"));
	 * // В консоль выведется: `A`
	 * // Так произойдёт потому что полное имя события `a+b` образует следующие *дополнительные 
	 * // имена*: `a`, `b`
	 * target.dispatchEvent(event);
	 * ```
	 * 
	 * #### Оператор `+`
	 * 
	 * Данный оператор образует *дополнительные имена* события в отрыве от *класса события*.
	 * 
	 * @example ```Typescript
	 * // Переменная объявдена но не инициализированна. Для удобства представте что 
	 * // здесь создаётся какой-нибудь произвольный от `SooperADS.EventTarget` объект
	 * declare const target: SooperADS.EventTarget;
	 * const event = new SooperADS.Event("a.a+b", target);
	 * 
	 * target.subscribe("a", () => console.log("A"));
	 * // В консоль выведется: `A`
	 * // Так произойдёт потому что полное имя события `a.a+b` образует следующие *дополнительные 
	 * // имена*: `a`, `a.a`, `b`
	 * target.dispatchEvent(event);
	 * ```
	 * 
	 * #### Оператор `.`
	 * 
	 * Данный оператор образует *подкласс события*. *Ксласс события* — Пространство имён, к тоторому 
	 * принадлежит событие. При этом классы могут быть многоуровнемыми.
	 * 
	 * @example ```Typescript
	 * // Переменная объявдена но не инициализированна. Для удобства представте что 
	 * // здесь создаётся какой-нибудь произвольный от `SooperADS.EventTarget` объект
	 * declare const target: SooperADS.EventTarget;
	 * const event = new SooperADS.Event("a.b.c", target);
	 * 
	 * target.subscribe("a", () => console.log("A"));
	 * target.subscribe("a.b", () => console.log("B"));
	 * target.subscribe("a.b.x", () => console.log("X"));
	 * 
	 * // В консоль выведется: `A`, `B`, но не `X`
	 * // Так произойдёт потому что полное имя события `a.b.c` образует следующие *дополнительные 
	 * // имена*: `a` (Нет классов), `a.b` (Класс `a` 1-го уровня), `a.b.c` (Класс `a` 2-го уровня 
	 * // (родитель класса `b`), класс `b` 1-го)
	 * target.dispatchEvent(event);
	 * ```
	 *
	 * #### Оператор `:`
	 * 
	 * Данный оператор образует *дополнительное имя* события с учётом его *класса*. (см #операто `.`)
	 * 
	 * @example ```Typescript
	 * // Переменная объявдена но не инициализированна. Для удобства представте что 
	 * // здесь создаётся какой-нибудь произвольный от `SooperADS.EventTarget` объект
	 * declare const target: SooperADS.EventTarget;
	 * const event = new SooperADS.Event("a.b:c", target);
	 * 
	 * target.subscribe("a", () => console.log("A"));
	 * target.subscribe("a.b", () => console.log("B"));
	 * target.subscribe("a.b.с", () => console.log("BС"));
	 * target.subscribe("a.с", () => console.log("С"));
	 * 
	 * // В консоль выведется: `A`, `B`, `C`, но не `BC`
	 * // Так произойдёт потому что полное имя события `a.b:c` образует следующие *дополнительные 
	 * // имена*: `a`, `a.b`, `a.c`
	 * target.dispatchEvent(event);
	 * ```
	 * 
	 * #### Оператор `^`
	 * 
	 * Данный оператор образует *дополнительное имя* события с учётом его *родительского класса*. (см #операто `.`)
	 * 
	 * @example ```Typescript
	 * // Переменная объявдена но не инициализированна. Для удобства представте что 
	 * // здесь создаётся какой-нибудь произвольный от `SooperADS.EventTarget` объект
	 * declare const target: SooperADS.EventTarget;
	 * const event = new SooperADS.Event("a.a.b^c", target);
	 * 
	 * target.subscribe("a.a", () => console.log("A"));
	 * target.subscribe("a.a.b", () => console.log("AB"));
	 * target.subscribe("a.c", () => console.log("AC"));
	 * target.subscribe("a.a.c", () => console.log("C"));
	 * 
	 * // В консоль выведется: `A`, `AB`, `C`, но не `AC`
	 * // Так произойдёт потому что полное имя события `a.a.b^c` образует следующие *дополнительные 
	 * // имена*: `a`, `a.a`, `a.a.b`, `a.c`
	 * target.dispatchEvent(event);
	 * ```
	 * 
	 * Также данный оператор может использоваться несколько раз подряд:
	 * 
	 * @example ```Typescript
	 * // Переменная объявдена но не инициализированна. Для удобства представте что 
	 * // здесь создаётся какой-нибудь произвольный от `SooperADS.EventTarget` объект
	 * declare const target: SooperADS.EventTarget;
	 * const event = new SooperADS.Event("a.a.b^^b", target);
	 * 
	 * target.subscribe("a.a", () => console.log("A"));
	 * target.subscribe("a.a.b", () => console.log("AB"));
	 * target.subscribe("a.b", () => console.log("B"));
	 * target.subscribe("b", () => console.log("!B"));
	 * 
	 * // В консоль выведется: `A`, `AB`, `!B`, но не `B`
	 * // Так произойдёт потому что полное имя события `a.a.b^c` образует следующие *дополнительные 
	 * // имена*: `a`, `a.a`, `a.a.b`, `b`
	 * target.dispatchEvent(event);
	 * ```
	 * 
	 * #### Оператор `/`
	 * 
	 * Данный оператор нечего не образует. Он нужен для разделения события на подвиды.
	 * 
	 * @example ```Typescript
	 * // Переменная объявдена но не инициализированна. Для удобства представте что 
	 * // здесь создаётся какой-нибудь произвольный от `SooperADS.EventTarget` объект
	 * declare const target: SooperADS.EventTarget;
	 * const event = new SooperADS.Event("a/b", target);
	 * 
	 * target.subscribe("a", () => console.log("A"));
	 * target.subscribe("a/b", () => console.log("B"));
	 * target.subscribe("a.b", () => console.log("AB"));
	 * 
	 * // В консоль выведется: `B`, но не `A`, `AB`
	 * // Так произойдёт потому что полное имя события `a/b` образует следующие *дополнительные 
	 * // имена*: `a/b`
	 * target.dispatchEvent(event);
	 * ```
	 * 
	 * ### Примечание A — Специальные значения
	 * 
	 * `"@error"` — В процессе валидации *поного имени* события произошла ошибка  
	 * `"@unset"` — *Полное имя* события не было указанно  
	 * `"@unknown"` — *Полное имя* события было указанно неправильно
	 * 
	 * @see {@link SooperADS.EventSubName}
	 */
	export type EventFullName = string;
	/** 
	 * Дополнительное имя события. Может содержать **ТОЛКО** символы англиского алфавита 
	 * в произвольном регистре, цифры и следующие спец. символы: `.` и `/`
	 * 
	 * *Дополнительное имя* события не может быть пустым
	 * 
	 * @see {@link SooperADS.EventFullName}
	 */
	export type EventSubName = string;

	export const EventSubNameChars: string[] = ((str: string): string[] => {
		let r: string[] = [];
		for (let i = 0; i < str.length; i++) {
			r.push(str[i]!);
			let t = str[i]!.toUpperCase();
			if (t !== str[i]) {
				r.push(t!);
			}
		}
		return r
	})("abcdefghijklmnopqrstuvwxyz_0123456789/.-");
	export const EventFullNameChars: string[] = [...EventSubNameChars, '+', '^', ':'];

	/**
	 * Проверяет валидность *дополнительного имени* события
	 * @param {EventSubName} name Имя для проверки
	 * @returns {boolean} `true` если имя валидно, иначе `false`
	 * @see {@link SooperADS.EventSubName}
	 */
	export function CheckEventSubname(name: EventSubName): boolean {
		let lastdot = false;
		if (name[0] == '.' || !name.length) {
			return false;
		}

		for (let i = 0; i < name.length; i++) {
			const ch = name[i];

			if (!charIs(ch, ...EventSubNameChars)) {
				return false;
			}
			lastdot = ch == '.';
		}
		return !lastdot;
	}

	/**
	 * Валидирует и парсит полное имя события
	 * @param {EventFullName} name Полное имя события
	 * @returns {"@unknown" | "@error" | EventSubName[]} `"@unknown"`, `"@unset"` или `"@error"`
	 * в случае ошибки, иначе вернёт массив *дополнительных имён*
	 * @see {@link SooperADS.EventFullName}
	 */
	export function PaseEventFullName(name: EventFullName): "@unset" | "@unknown" | "@error" | EventSubName[] {
		if (!name) {
			return "@unset";
		}

		let sub: EventSubName[] = [];
		let classes: EventSubName[] = [];
		let cur: string = "";

		let lastupper = false;

		function __classes_fn(): string {
			let r: string = "";

			for (let i = 0; i < classes.length; i++) {
				r += classes[i]! + '.';
			}

			return r + cur;
		}

		for (let i = 0; i < name.length; i++) {
			const ch = name[i];

			if (charIs(ch, '.', '+', ':')) {
				if (!cur.length) {
					return "@unknown";
				}
				else {
					sub.push(__classes_fn());
					if (ch == '+') {
						classes = [];
					}
					else if (ch !== ':') {
						classes.push(cur);
					}
					cur = "";
				}
			}
			else if (ch === '^') {
				if (!cur.length && !lastupper || !classes.length) {
					return "@unknown";
				}
				else {
					if (cur) {
						sub.push(__classes_fn());
					}
					classes.pop();
					cur = "";
				}
			}
			else {
				if (!charIs(ch, ...EventFullNameChars)) {
					return "@error";
				}
				cur += ch;
			}

			lastupper = ch == '^';
		}
		sub.push(__classes_fn());

		return sub.filter(function (item, pos) {
			return sub.indexOf(item) == pos;
		});
	}

	/**
	 * Событие **[АДЕКВАТНОЕ]**
	 * 
	 * ### Слоты
	 * 
	 * | Имя                  | Наследование  | Назначение                                                 |
	 * |----------------------|---------------|------------------------------------------------------------|
	 * | `PrototypeChangeble` | `never`       | Определяет, возможно ли изменить прототип объекта          |
	 * | `EventState`         | `never`       | Состояние события                                          |
	 * | `EventLastResult`    | `always`      | Определяет значение свойства `result`. Может отсутствовать |
	 * | `EventAllowDefault`  | `overridable` | Определяет, разрешеноли поведение по умолчанию             |
	 */
	@$safedConstructor
	export class Event<Result = undefined> extends Base {
		/**
		 * Целевой объект события
		 */
		declare readonly target: EventTarget;
		/**
		 * Полное имя события
		 * 
		 * @see {@link SooperADS.EventFullName}
		 */
		declare readonly name: EventFullName;
		/**
		 * Дополнительные имкена события
		 * 
		 * @see {@link SooperADS.EventSubName}
		 */
		declare readonly subnames: EventSubName[];
		/**
		 * Состояние обработки события
		 * @see {@link SooperADS.EventState}
		 */
		declare readonly state: EventState;

		/**
		 * Отметка во времени, указывающая когда было созданно событие
		 */
		declare readonly timestamp: Date;

		/**
		 * Определяет, есть ли промежуточный результат бработки
		 */
		declare readonly hasResult: boolean;
		/**
		 * Равно `true` если состояние события равно `init` или `work`, иначе `false`
		 */
		declare readonly workable: boolean
		/**
		 * Промежуточный результат обработки. Каждый обработчик может работать с ним
		 */
		declare result: Result | undefined;

		/**
		 * Создаёт объект SooperADS.Event
		 * @param {EventTarget} target Цель события
		 * @param {EventFullName} name Полное имя события
		 * 
		 * > **ВАЖНО**: Полное имя события может содержать **ТОЛКО** символы англиского алфавита 
		 * в произвольном регистре, цифры, '-', '_' и следующие спец. символы: `.`, `+`, `/`, `^` и `:`
		 * 
		 * @see подробнее см. {@link SooperADS.Event.prototype.name SooperADS.Event@name}
		 */
		constructor(target: EventTarget, name: EventFullName) {
			let sname: string = "@unset";
			super();

			let sub = PaseEventFullName(name);
			if (sub instanceof Array) {
				sname = name;
				_INIT_SLOT<EventState>(this, Private.EVENT_OBJECT, "EventState", "init", false);
			}
			else {
				sname = sub;
				_INIT_SLOT<EventState>(this, Private.EVENT_OBJECT, "EventState", "error", true);
			}

			_INIT_SLOT(this, Private.BASE_OBJECT, "PrototypeChangeble", false, true);
			_INIT_SLOT<boolean>(this, Private.EVENT_OBJECT, "EventAllowDefault", true, false);

			Object.defineProperties(this, {
				"target": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: target
				},
				"name": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: sname
				},
				"subnames": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: sub instanceof Array ? sub : []
				},
				"timestamp": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: new Date()
				},
			});

			this.prototypeFreeze();
		}

		/**
		 * @returns {boolean} Запрещено ли поведение по умолчанию
		 */
		defaultNotAllow(): boolean {
			let r = _GET_SLOT<boolean>(this, Private.EVENT_OBJECT, "EventAllowDefault");

			if (r === undefined) {
				r = true;
				_SET_SLOT<boolean>(this, Private.EVENT_OBJECT, "EventAllowDefault", true);
			}
			return !r;
		}
		/**
		 * Запретить выполнения поведения по умолчанию.
		 */
		preventDefault(): void {
			_SET_SLOT<boolean>(this, Private.EVENT_OBJECT, "EventAllowDefault", false);
		}

		/**
		 * Проверяет, соответствует ли события *дополнительному имени*
		 * @param {EventSubName} name Имя для проверки
		 * @returns {boolean} `true` если соответствует, иначе `false`
		 */
		nameIs(name: EventSubName): boolean {
			if (!CheckEventSubname(name)) {
				return false;
			}

			return charIs(name, ...this.subnames);
		}

		/**
		 * Прервать обработку события
		 */
		stopEventWork(): void {
			_SET_SLOT<EventState>(this, Private.EVENT_OBJECT, "EventState", "break");
		}
		/**
		 * @returns {boolean} Обррабатывается ли сейчас данное событие
		 */
		eventInWork(): boolean {
			return this.state === "work";
		}

		/**
		 * @deprecated
		 * @returns `this.target`
		 */
		targetAs<Target extends EventTarget>(): Target {
			return this.target as Target;
		}

		/**
		 * Выполнет обработку текущего события
		 * @param {EventFullName} as Алтернативное *полное имя* события
		 * @see {@link SooperADS.EventTarget.prototype.dispatchEvent}
		 * @see {@link SooperADS.EventFullName}
		 */
		dispatch(as: EventFullName | null = null): boolean {
			if (this.target) {
				return this.target.dispatchEvent(this as any, as);
			}
			return false;
		}
		/**
		 * Проверяет, поддерживается ли данное событие целью событие
		 * @returns {boolean} `true` если событие поодерживается, иначе `false`
		 */
		targetSupport(): boolean {
			const evl = this.target.supportedEvents;
			for (let i = 0; i < evl.length; i++) {
				if (evl[i] == this.name) {
					return true;
				}
			}
			return false;
		}

		/**
		 * @returns {number} Пормежуток времени, в миллисикундах, с момента создание данного события
		 */
		timestampDelta(): number {
			return Date.now() - this.timestamp.getTime();
		}
	}
	Object.defineProperties(Event.prototype, {
		"state": {
			configurable: false,
			enumerable: true,
			get: function state(this: Event): EventState {
				let r = _GET_SLOT<EventState>(this, Private.EVENT_OBJECT, "EventState");

				if (!r || !charIs(r, "break", "init", "release", "work")) {
					r = "unknown";
					_SET_SLOT<EventState>(this, Private.EVENT_OBJECT, "EventState", "unknown");
				}

				return r;
			}
		},
		"hasResult": {
			configurable: false,
			enumerable: true,
			get: function hasResult(this: Event): boolean {
				return _HAS_SLOT(this, Private.EVENT_OBJECT, "EventLastResult");
			}
		},
		"result": {
			configurable: false,
			enumerable: true,
			get: function result(this: Event): any {
				return _GET_SLOT<any>(this, Private.EVENT_OBJECT, "EventLastResult");
			},
			set: function result(this: Event, v: any): void {
				if (this.state === "error") {
					return;
				}

				if (!this.hasResult) {
					_INIT_SLOT(
						this, Private.EVENT_OBJECT, "EventLastResult", v, false
					);
				}
				else {
					_SET_SLOT(
						this, Private.EVENT_OBJECT, "EventLastResult", v
					);
				}
			}
		},
		"workable": {
			configurable: false,
			enumerable: true,
			get: function canWork(this: Event): boolean {
				return charIs(this.state, "work", "init");
			},
		}
	});
	//#endregion

	//#region EventObject
	/**
	 * Обработчик события
	 */
	export type EventWorker<Target extends EventTarget, EventArgs extends Event<EventResult>, EventResult> =
		(this: Target, event: EventArgs) => any | void;

	/**
	 * Дескриптор обработчика событий
	 */
	export interface EventWorkerObject<Target extends EventTarget, EventArgs extends Event<EventResult>, EventResult = undefined> {
		/**
		 * Обработчик
		 */
		readonly worker: EventWorker<Target, EventArgs, EventResult>;
		/**
		 * Определяет, будет ли возможность отменить обработку поведения по умолчанию для данного обработчика
		 */
		readonly passive: boolean;
		/**
		 * Одноразовый ли обработчик. Одноразовые обработчики самоудаляются после выполнения
		 */
		readonly once: boolean;
	}

	/**
	 * Объект управления обработчиками события
	 */
	@$noPrototype @$safedConstructor
	class EventObject<EVS extends Event<ERT>, ERT = undefined> {
		/**
		 * Создаёт объект управления обработчиками события
		 * @param {boolean} passive Отключает отмену обработки поведения по умолчанию для ВСЕХ обработчиков данного события
		 * @param {EventWorker<EventTarget, EVS, ERT> | null} defaultw Функция, определяющая поведение по умолчанию.
		 * Выполнится после каждой обработке события, если не отменено поведение по умолчанию
		 * @param {boolean} async Если равно `true`, выполняет обработку событий в асинхронном режиме
		 */
		constructor(passive: boolean, defaultw: EventWorker<EventTarget, EVS, ERT> | null = null, async: boolean = true) {
			Object.defineProperties(this, {
				"default": {
					configurable: false,
					enumerable: true,
					writable: true,
					value: defaultw
				},
				"passive": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: passive
				},
				"async": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: async
				},
				"workers": {
					configurable: false,
					enumerable: false,
					writable: false,
					value: Object.create(null, {
						"last": {
							configurable: false,
							enumerable: false,
							writable: true,
							value: -1
						}
					})
				}
			});
			Object.preventExtensions(this);
		}

		/**
		 * Функция, определяющая поведение по умолчанию.
		 * Выполнится после каждой обработке события, если не отменено поведение по умолчанию
		 */
		declare default: EventWorker<EventTarget, EVS, ERT> | null;

		/**
		 * Если равно `true`, выполняет обработку событий в асинхронном режиме
		 */
		declare readonly async: boolean;
		/**
		 * Отключает отмену обработки поведения по умолчанию для ВСЕХ обработчиков данного события
		 */
		declare readonly passive: boolean;
		/**
		 * Зарегестрированные обработчики
		 */
		declare readonly workers: {
			[i: number]: EventWorkerObject<EventTarget, EVS, ERT>;
			last: number;
		};

		/**
		 * Регестрирует заданный обработчик
		 * @param {EventWorker<EventTarget, EVS, ERT>} w Обработчик
		 * @param {boolean} once Одноразовый ли обработчик
		 * @param {boolean} passive Пасивен ли он
		 * @see {@link SooperADS.EventWorkerObject}
		 * @returns {number | null} `null` если ошибка, иначе вернёт ID зарегестрированного обработчика
		 */
		addWorker(w: EventWorker<EventTarget, EVS, ERT>, once: boolean = false, passive: boolean = false): number | null {
			if (!w || !(w instanceof Function)) {
				return null;
			}

			let i = this.workers.last + 1;

			this.workers[i] = Object.create(null, {
				"worker": {
					configurable: false,
					enumerable: false,
					writable: false,
					value: w
				},
				"passive": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: passive
				},
				"once": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: once
				},
			});
			this.workers.last++;
			return i;
		}
		/**
		 * Удаляет обработчик по указанному ID
		 * @param {number} token ID обработчика
		 * @returns {boolean} `true если всё ок, иначе `false`
		 */
		removeWorker(token: number): boolean {
			if (Object.hasOwn(this.workers, token)) {
				return delete this.workers[token];
			}
			return false;
		}

		/**
		 * Обрабатывает указанное событие
		 * @param {EVS} ev Событе
		 */
		dispatch(ev: EVS): void {
			function __work(this: EventObject<EVS, ERT>, works: EventWorkerObject<EventTarget, EVS, ERT>[]): void {
				_SET_SLOT<EventState>(ev, Private.EVENT_OBJECT, "EventState", "work");

				const evdas = !ev.defaultNotAllow();

				for (let i = 0; i < works.length; i++) {
					const wf = works[i]!;

					const res = wf.worker.call(ev.target, ev);

					if (res !== undefined) {
						ev.result = res;
					}

					if (!ev.workable) {
						_SET_SLOT<EventState>(ev, Private.EVENT_OBJECT, "EventState", "break");
						break;
					}

					if (wf.passive && evdas) {
						_SET_SLOT<boolean>(ev, Private.EVENT_OBJECT, "EventAllowDefault", true);
					}
					if (wf.once) {
						this.removeWorker(i);
					}
				}

				if (ev.state !== "break") {
					_SET_SLOT<EventState>(ev, Private.EVENT_OBJECT, "EventState", "release");
				}

				if ((!ev.defaultNotAllow() || this.passive) && this.default) {
					this.default.call(ev.target, ev);
				}
			}

			if (ev.state == "error") {
				return;
			}

			let w: EventWorkerObject<EventTarget, EVS, ERT>[] = [];
			for (const key in this.workers) {
				const wfx = this.workers[key];

				if (wfx?.worker) {
					w.push(wfx);
				}
			}

			if (!w.length) {
				return;
			}

			if (this.async) {
				new Promise<void>(r => {
					r(__work.call(this, w));
				})
			}
			else {
				__work.call(this, w);
			}
		}
	}

	interface EventTarget_OnEvent {
		[key: string]: EventObject<any>
	}

	/**
	 * Карта событий. Определяет тип и имя события
	 */
	export interface EventBaseMap {
		readonly [v: string]: Event<any>;
	}
	//#endregion

	//#region EventTarget
	/**
	 * Токен обработчика события
	 */
	export interface EventObjectToken<EvName extends PropertyKey, Owner extends EventTarget> {
		/**
		 * ID
		 */
		readonly index: number;
		/**
		 * *Дополнительное имя* события
		 */
		readonly name: EvName;
		/**
		 * Объет, к которому привязан обработчик
		 */
		readonly owner: Owner;
	}

	Object.defineProperty(Private, "GetEvent", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: Slot(null, "GetEvent")
	});
	Object.defineProperty(Private, "DispatchEvent", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: Slot(null, "DispatchEvent")
	});

	/**
	 * Базовый классы для объектов, которые должны поддерживать события
	 * 
	 * ### Слоты
	 * 
	 * | Имя               | Наследование  | Назначение                                                     |
	 * |-------------------|---------------|----------------------------------------------------------------|
	 * | `OnEvent`         | `never`       | Хранилище обраработчиков событий для данного объекта           |
	 * | `EventExtendable` | `never`       | Определяет, можно ли добавлят поддержку новых событий в объект |
	 * | `EventActive`     | `never`       | Определяет, будут ли события обрабатываться                    |
	 */
	@$safedConstructor
	export class EventTarget<EventOn extends EventBaseMap = {}> extends Base {
		/**
		 * Конструирует объет цели события
		 * @param {boolean} activeEventDispatch Будут ли сейчас обрабвтаться события
		 */
		constructor(activeEventDispatch: boolean) {
			super();

			_INIT_SLOT(this, Private.EVENT_TARGET_BASE_OBJECT, "OnEvent", Object.create(null), true);
			_INIT_SLOT(this, Private.EVENT_TARGET_BASE_OBJECT, "EventExtendable", true, false);
			_INIT_SLOT(this, Private.EVENT_TARGET_BASE_OBJECT, "EventActive", activeEventDispatch, false);

			this.prototypeFreeze()
		}

		/**
		 * Будут ли сейчас обрабвтаться события  
		 *   
		 * >**СОВЕТ**: Используйте данное свойство для отключения событий в конструкторе
		 */
		get activeEventsDispatch(): boolean {
			return _GET_SLOT(this, Private.EVENT_TARGET_BASE_OBJECT, "EventActive") ?? true;
		}
		set activeEventsDispatch(v: boolean) {
			_SET_SLOT(this, Private.EVENT_TARGET_BASE_OBJECT, "EventActive", v);
		}

		/**
		 * Список поддерживаемых событий
		 */
		declare readonly supportedEvents: string[];
		/**
		 * Указывает на возможностьдобавления поддержки новых событий  
		 *   
		 * Если равно `false` — новые события добавлять нельзя
		 */
		declare readonly eventsExtendable: boolean;

		/**
		 * Создаёт поддержу событие
		 * @param {Key} name Имя событие
		 * @param {boolean} passive Отключает отмену обработки поведения по умолчанию для ВСЕХ обработчиков данного события
		 * @param {boolean} async Если равно `true`, выполняет обработку событий в асинхронном режиме
		 * @param {EventWorker<this, EventOn[Key], unknown>} defaultw Обработчик по умолчанию
		 * @returns {boolean} `true` если всё ок, иначе `false`
		 */
		defineEvent<Key extends keyof EventOn>(
			name: Key, passive: boolean, async: boolean, defaultw: EventWorker<this, EventOn[Key], unknown> | null
		): boolean
		/**
		 * Создаёт поддержу событие
		 * @param {string} name *Дополнительное имя* событие
		 * @param {boolean} passive Отключает отмену обработки поведения по умолчанию для ВСЕХ обработчиков данного события
		 * @param {boolean} async Если равно `true`, выполняет обработку событий в асинхронном режиме
		 * @param {EventWorker<this, EventT, RT>} defaultw Обработчик по умолчанию
		 * @returns {boolean} `true` если всё ок, иначе `false`
		 */
		defineEvent<EventT extends Event<RT>, RT = undefined>(
			name: string, passive: boolean, async: boolean, defaultw: EventWorker<this, EventT, RT> | null
		): boolean
		defineEvent<EventT extends Event<RT>, RT = undefined>(
			name: string, passive: boolean, async: boolean, defaultw: EventWorker<this, EventT, RT> | null
		): boolean {
			if (this.hasEventSupport(name) || !this.eventsExtendable || !CheckEventSubname(name)) {
				return false;
			}

			let slot = _GET_SLOT<EventTarget_OnEvent>(
				this, Private.EVENT_TARGET_BASE_OBJECT, "OnEvent"
			)!;

			let o = new EventObject<EventT, RT>(passive, defaultw as any, async);
			Object.defineProperty(slot, name, {
				configurable: false,
				enumerable: true,
				writable: false,
				value: o
			});

			return true;
		}
		/**
		 * Проверяет, поддерживаются ли события с заднным именем
		 * @param {string} name Дополнительное имя событие
		 * @returns {boolean} `true` если поддерживается, иначе `false`
		 */
		hasEventSupport(name: string): boolean
		/**
		 * Проверяет, поддерживаются ли события с заднным именем
		 * @param {Key} name Имя событие
		 * @returns {boolean} `true` если поддерживается, иначе `false`
		 */
		hasEventSupport<Key extends keyof EventOn>(name: Key): true
		hasEventSupport(name: string): boolean {
			let slot = _GET_SLOT<EventTarget_OnEvent>(
				this, Private.EVENT_TARGET_BASE_OBJECT, "OnEvent"
			)!;

			return Object.hasOwn(slot, name);
		}

		/**
		 * Получает *контроллер* событий для указанногото типа
		 * @param {string} name Тип событий
		 * @returns {EventObject<EventT, RT> | undefined} `undefined` если событие данного типа не поддерживается
		 */
		[Private.GetEvent]<EventT extends Event<RT>, RT = undefined>(name: string): EventObject<EventT, RT> | undefined {
			let slot = _GET_SLOT<EventTarget_OnEvent>(
				this, Private.EVENT_TARGET_BASE_OBJECT, "OnEvent"
			)!;

			return slot[name];
		}

		/**
		 * Подписывает обработчик на события
		 * @param {Key} name Имя событий
		 * @param {EventWorker<this, EventOn[Key], unknown>} work Обработчик
		 * @param {?boolean} once Одноразовый ли обработчик
		 * @param {?boolean} passive Пасивен ли обработчик
		 * @returns {EventObjectToken<Key, this> | undefined} Токен обработчика события 
		 * (указатель на обработчик) или, в случае ошибки, вернёт `undefined`
		 * @see {@link SooperADS.EventWorkerObject} и {@link SooperADS.EventObjectToken}
		 */
		subscribeEvent<Key extends keyof EventOn>(
			name: Key, work: EventWorker<this, EventOn[Key], unknown>, once?: boolean, passive?: boolean
		): EventObjectToken<Key, this> | undefined
		/**
		 * Подписывает обработчик на события
		 * @param {string} name *Дополнительное имя* событий
		 * @param {EventWorker<this, EventT, RT>} work Обработчик
		 * @param {?boolean} once Одноразовый ли обработчик
		 * @param {?boolean} passive Пасивен ли обработчик
		 * @returns {EventObjectToken<string, this> | undefined} Токен обработчика события 
		 * (указатель на обработчик) или, в случае ошибки, вернёт `undefined`
		 * @see {@link SooperADS.EventWorkerObject} и {@link SooperADS.EventObjectToken}
		 */
		subscribeEvent<EventT extends Event<RT>, RT = undefined>(
			name: string, work: EventWorker<this, EventT, RT>, once?: boolean, passive?: boolean
		): EventObjectToken<string, this> | undefined
		subscribeEvent<EventT extends Event<RT>, RT = undefined>(
			name: string, work: EventWorker<this, EventT, RT>, once: boolean = false, passive: boolean = false
		): EventObjectToken<string, this> | undefined {
			let ev = this[Private.GetEvent]<EventT, RT>(name);
			let st = ev?.addWorker(work as any, once, passive);

			if (st == null) {
				return undefined;
			}

			return Object.create(null, {
				"index": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: st
				},
				"name": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: name
				},
				"owner": {
					configurable: false,
					enumerable: false,
					writable: false,
					value: this
				},
			});
		}
		/**
		 * Описывает обработчик от события
		 * @param {EventObjectToken<string, this>} token Токен обработчика
		 * @returns {boolean} `true` если всё ок, иначе `false`
		 */
		unsubscribeEvent(token: EventObjectToken<string, this>): boolean
		/**
		 * Описывает обработчик от события
		 * @param {EventObjectToken<Key, this>} token Токен обработчика
		 * @returns {boolean} `true` если всё ок, иначе `false`
		 */
		unsubscribeEvent<Key extends keyof EventOn>(token: EventObjectToken<Key, this>): boolean
		unsubscribeEvent(token: EventObjectToken<string, this>): boolean {
			if (token.owner !== this) {
				return false
			}

			let ev = this[Private.GetEvent]<any, any>(token.name);
			return Boolean(ev?.removeWorker(token.index));
		}

		/**
		 * Выполняет обработку события. НЕ ПРОВЕРЯЕТ ПАРАМЕТРЫ
		 * @param {EventT} event Событие для обработки
		 * @param {EventSubName[]} names *Дополнительные имена* события
		 */
		[Private.DispatchEvent](event: Event<unknown>, names: EventSubName[]): void {
			names.forEach(evon => {
				this[Private.GetEvent]<Event<unknown>, unknown>(evon)?.dispatch(event);
			})
		}

		/**
		 * Выполняет обработку заданного события
		 * @param {EventOn[Key]} event Событие для обработки
		 * @param {?Key | null} as Алтернативное полное имя события. Указывает под каким именем обрабатывать событие
		 */
		dispatchEvent<Key extends keyof EventOn>(event: EventOn[Key], as?: Key | null): boolean
		/**
		 * Выполняет обработку заданного события
		 * @param {EventOn[Key]} event Событие для обработки
		 * @param {EventFullName} as Алтернативное полное имя события. Указывает под каким именем обрабатывать событие
		 * @see {@link SooperADS.EventFullName}
		 */
		dispatchEvent<Key extends keyof EventOn>(event: EventOn[Key], as: EventFullName | EventSubName[]): boolean
		/**
		 * Выполняет обработку заданного события
		 * @param {EventT} event Событие для обработки
		 * @param {?EventSubName | null} as Алтернативное полное имя события. Указывает под каким именем обрабатывать событие
		 * @see {@link SooperADS.EventFullName}
		 */
		dispatchEvent<EventT extends Event<RT>, RT = undefined>(
			event: EventT, as?: EventFullName | EventSubName[] | null
		): boolean
		dispatchEvent<EventT extends Event<RT>, RT = undefined>(
			event: EventT, as: EventFullName | EventSubName[] | null = null
		): boolean {
			if (!this.activeEventsDispatch && event.subnames.length) {
				return false;
			}

			let evNames: EventSubName[] = event.subnames;
			if (as) {
				if (as instanceof Array) {
					for (let i = 0; i < as.length; i++) {
						if (!CheckEventSubname(as[i]!)) {
							return false;
						}
					}
					evNames = as;

					if (!as.length) {
						evNames = event.subnames;
					}
				}
				else {
					let cns = PaseEventFullName(as);
					if (cns instanceof Array) {
						evNames = cns;
					}
					else {
						return false;
					}
				}
			}

			this[Private.DispatchEvent](event, evNames);
			return true;
		}

		/**
		 * Запрещает добавление поддержки новых событий
		 */
		preventEventsExtention(): void {
			_INIT_SLOT<boolean>(
				this, Private.EVENT_TARGET_BASE_OBJECT, "EventExtendable", false, true
			);
		}
	}

	EventTarget.defineProperty("supportedEvents", {
		configurable: false,
		enumerable: true,
		get: function definedEvents(this: EventTarget): string[] {
			const slot = _GET_SLOT<EventTarget_OnEvent>(
				this, Private.EVENT_TARGET_BASE_OBJECT, "OnEvent"
			)!;

			let r: string[] = [];
			for (const key in slot) {
				r.push(key)
			}

			return r;
		},
	})
	EventTarget.defineProperty("eventsExtendable", {
		configurable: false,
		enumerable: true,
		get: function canDefineEvent(this: EventTarget): boolean {
			return Boolean(_GET_SLOT<boolean>(
				this, Private.EVENT_TARGET_BASE_OBJECT, "EventExtendable"
			));
		}
	});
	//#endregion
	//#endregion

	//#region Дополнительные объекты
	//#region BitColor
	export interface BitColorOptions {
		/**
		 * Запрещает изменение цвета
		 * @default false
		 */
		readonly?: boolean;
		/**
		 * Определяет, поддерживается ли прозрачность
		 * @default true
		 */
		alpha?: boolean;
	}
	export interface ColorBase {
		/**Прозрачность [**МИН**: `0`; **МАКС**: `255`]*/
		alpha: number;
	}

	/**
	 * Представление цвета в HSV формате
	 */
	export interface HSVColor extends ColorBase {
		/**Оттенок [**МИН**: `0`; **МАКС**: `360`]*/
		hue: number;
		/**Насыщенность [**МИН**: `0`; **МАКС**: `255`]*/
		saturation: number;
		/**Цвет [**МИН**: `0`; **МАКС**: `255`]*/
		value: number;
	}
	/**
	 * Представление цвета в RGN формате
	 */
	export interface RGBColor extends ColorBase {
		/**Красный [**МИН**: `0`; **МАКС**: `255`]*/
		red: number;
		/**Зелёный [**МИН**: `0`; **МАКС**: `255`]*/
		green: number;
		/**Синий [**МИН**: `0`; **МАКС**: `255`]*/
		blue: number;
	}
	/**
	 * Представление цвета в CYM формате (**C**yan, **M**agenta, **Y**ellow)
	 */
	export interface CYMColor extends ColorBase {
		/**Голубой [**МИН**: `0`; **МАКС**: `255`]*/
		cyan: number;
		/**Жёлтый [**МИН**: `0`; **МАКС**: `255`]*/
		yellown: number;
		/**Пурпурный [**МИН**: `0`; **МАКС**: `255`]*/
		magenta: number;
	}
	export interface MonochromeColor extends ColorBase {
		/**Цвет [**МИН**: `0`; **МАКС**: `255`]*/
		value: number;
	}

	/**
	 * Формат цветового представления
	 */
	export type ColorFormat = "Monochrome" | "RGB" | "HSV" | "CYM"
	/**
	 * Цветовой филтер
	 */
	export type ColorFilter = (v: BitColor, alpha: boolean) => BitColor;

	type BitColor_RandomBase = {
		base?: BitColorOptions & {
			filter?: ColorFilter | null;
		};
		alpha?: {
			allow?: false;
		} | {
			allow: true;
			min: number;
			max: number;
		};
	};

	export type ColorRandomOptionsMonochrome = BitColor_RandomBase & {
		limits?: {
			minValue?: number;
			maxValue?: number;
		}
	};
	export type ColorRandomOptionsRGB = BitColor_RandomBase & {
		limits?: {
			minRed?: number;
			minBlue?: number;
			minGreen?: number;

			maxRed?: number;
			maxGreen?: number;
			maxBlue?: number;
		}
	};
	export type ColorRandomOptionsCYM = BitColor_RandomBase & {
		limits?: {
			minCyan?: number;
			minYellow?: number;
			minMagenta?: number;

			maxCyan?: number;
			maxYellow?: number;
			maxMagenta?: number;
		}
	};
	export type ColorRandomOptionsHSV = BitColor_RandomBase & {
		limits?: {
			minHue?: number;
			minValue?: number;
			minSaturation?: number;

			maxHue?: number;
			maxValue?: number;
			maxSaturation?: number;
		}
	};

	export type ColorRandomOptions = ColorRandomOptionsMonochrome | ColorRandomOptionsRGB |
		ColorRandomOptionsHSV | ColorRandomOptionsCYM;

	/**
	 * Цвет в RGBA формате
	 * 
	 * ### Слоты
	 * 
	 * | Имя          | Наследование | Назначение                                      |
	 * |--------------|--------------|-------------------------------------------------|
	 * | `Binary`     | `never`      | Бинарное представление цвета                    |
	 * | `AllowAlpha` | `never`      | Определяет, разрешена ли прозрачность для цвета |
	 */
	@$safedConstructor
	export class BitColor extends BaseWritable {
		/**
		 * Создаёт цвет
		 * @param {?number | undefined} v Цвет в бинарном представлении. По умолчанию равен `0`
		 * @param {?BitColorOptions} options Опции создания цвета
		 */
		constructor(v?: number | undefined, options: BitColorOptions = {}) {
			super();
			const copts: Required<BitColorOptions> = {
				alpha: options.alpha ?? true,
				readonly: options.readonly ?? false
			};

			_INIT_SLOT(this, Private.BIT_COLOR_OBJECT, "Binary", 0, copts.readonly);
			_INIT_SLOT(this, Private.BIT_COLOR_OBJECT, "AllowAlpha", copts.alpha, copts.readonly);

			if (v) {
				this.setBinary(v);
			}
		}

		/**
		 * @returns {number} бинарное представление
		 */
		getBinary(): number {
			return _GET_SLOT(this, Private.BIT_COLOR_OBJECT, "Binary") ?? 0;
		}
		setBinary(v: number): void {
			if (typeof (v) === "number") {
				_SET_SLOT(this, Private.BIT_COLOR_OBJECT, "Binary", v & 0xFFFFFFFF)
			}
		}

		/**
		 * @returns {number} бинарное представление без учёта прозрачности
		 */
		getSolidBinary(): number {
			return (
				_GET_SLOT<number>(this, Private.BIT_COLOR_OBJECT, "Binary") ?? 0
			) | BitColor.BIN_ALPA_MASK;
		}
		/**
		 * @param {number} v Новое значение цвета в бинарнлм виде. Не учитывает прозрачность
		 */
		setSolidBinary(v: number): void {
			if (typeof (v) === "number") {
				v |= BitColor.BIN_ALPA_MASK;
				_SET_SLOT(this, Private.BIT_COLOR_OBJECT, "Binary", v & 0xFFFFFFFF)
			}
		}

		/**
		 * Указывает,разрешена ли прозрачность
		 */
		get allowAlpha(): boolean {
			return _GET_SLOT(this, Private.BIT_COLOR_OBJECT, "AllowAlpha") ?? true;
		}
		set allowAlpha(v: boolean) {
			v = Boolean(v);
			_SET_SLOT(this, Private.BIT_COLOR_OBJECT, "AllowAlpha", v);
		}

		/**
		 * @returns {BitColor} Полная копия данного объекта
		 */
		copy(): BitColor {
			let r = new BitColor(0, {
				alpha: this.allowAlpha,
				readonly: !this.writtable
			});
			r.setBinary(this.getBinary());
			return r;
		}
		/**
		 * @param {boolean} alpha Разрешена ли прозрачность в копии
		 * @returns {BitColor} Копия значения данного объекта
		 */
		copyValue(alpha: boolean = true): BitColor {
			return new BitColor(this.getBinary(), {
				alpha: alpha,
				readonly: false
			});
		}
		/**
		 * Устанавлевает значение цвета
		 * @param {number} red Красный
		 * @param {number} green Зелёный
		 * @param {number} blue Синий
		 * @param {number} alpha Прозрачность
		 */
		set(red: number, green: number, blue: number, alpha: number = BitColor.MAX_COMPONENT_VALUE): void {
			if (!this.allowAlpha) {
				alpha = BitColor.MAX_COMPONENT_VALUE;
			}

			this.setBinary(BitColor.binary({
				red: red,
				green: green,
				blue: blue,
				alpha: alpha
			}));
		}
		/**
		 * Устанавлевает значение цвета
		 * @param {number} v Красный, зелёный и синий одновременно
		 * @param {number} a Прозрачность
		 */
		setAll(v: number, a: number = BitColor.MAX_COMPONENT_VALUE): void {
			this.set(v, v, v, a);
		}

		override preventChange(): void {
			super.preventChange();
			_INIT_SLOT(this, Private.BIT_COLOR_OBJECT, "Binary", this.getBinary(), true);
			_INIT_SLOT(this, Private.BIT_COLOR_OBJECT, "AllowAlpha", this.allowAlpha, true);
		}
		/**
		 * Сбрасывает значение цвета
		 * 
		 * @deprecated
		 */
		reset(): void {
			this.setBinary(0);
		}

		/**
		 * @returns {BitColor} Негатив данного цвета
		 */
		negative(): BitColor {
			let r = new BitColor(~this.getBinary(), {
				alpha: this.allowAlpha
			});
			r.alpha = this.alpha;
			return r;
		}
		/**
		 * @returns {BitColor} Срднее значение цвета (`red` = `green` = `blue` = `value`)
		 */
		monochrome(): BitColor {
			let r = new BitColor(0, {
				alpha: this.allowAlpha
			});

			const v = (this.red + this.green + this.blue) / 2;
			r.setAll(v, this.alpha);
			return r;
		}

		/**
		 * Красный канал
		 */
		get red(): number {
			return this.getBinary() & BitColor.BIN_RED_MASK;
		}
		set red(v: number) {
			this.setBinary((this.getBinary() & ~BitColor.BIN_RED_MASK) | (v & BitColor.MAX_COMPONENT_VALUE));
		}
		/**
		 * Зелёный канал
		 */
		get green(): number {
			return (this.getBinary() & BitColor.BIN_GREEN_MASK) >> 8;
		}
		set green(v: number) {
			this.setBinary((this.getBinary() & ~BitColor.BIN_GREEN_MASK) | ((v & BitColor.MAX_COMPONENT_VALUE) << 8));
		}
		/**
		 * Синий канал
		 */
		get blue(): number {
			return (this.getBinary() & BitColor.BIN_BLUE_MASK) >> 16;
		}
		set blue(v: number) {
			this.setBinary((this.getBinary() & ~BitColor.BIN_BLUE_MASK) | ((v & BitColor.MAX_COMPONENT_VALUE) << 16));
		}
		/**
		 * Канал прозрачности
		 */
		get alpha(): number {
			if (!this.allowAlpha) {
				return BitColor.MAX_COMPONENT_VALUE;
			}
			return (this.getBinary() & BitColor.BIN_ALPA_MASK) >>> 24;
		}
		set alpha(v: number) {
			if (!this.allowAlpha) {
				v = BitColor.MAX_COMPONENT_VALUE;
			}
			this.setBinary((this.getBinary() & ~BitColor.BIN_ALPA_MASK) | ((v & BitColor.MAX_COMPONENT_VALUE) << 24));
		}

		override toString(): string {
			let r = this.red.toString(16);
			if (r.length == 1) {
				r = '0' + r;
			}

			let g = this.green.toString(16);
			if (g.length == 1) {
				g = '0' + g;
			}

			let b = this.blue.toString(16);
			if (b.length == 1) {
				b = '0' + b;
			}

			let a = this.alpha.toString(16);
			if (a.length == 1) {
				a = '0' + a;
			}

			if (a.toLowerCase() === "FF") {
				return `#${r}${g}${b}`;
			}

			return `#${r}${g}${b}${a}`;
		}

		/**
		 * @returns {RGBColor} Представление объекта в RGB формате
		 */
		toRGB(): RGBColor {
			return Object.create(null, {
				"red": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: this.red
				},
				"green": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: this.green
				},
				"blue": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: this.blue
				},
				"alpha": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: this.alpha
				}
			});
		}
		/**
		 * @returns {HSVColor} Представление объекта в HSV формате
		 */
		toHSV(): HSVColor {
			const r = this.red;
			const g = this.green;
			const b = this.blue;

			const min = Math.min(r, g, b);
			const max = Math.max(r, g, b);

			const dm = max - min;

			let h: number;
			if (max === min) {
				h = 0;
			}
			else if (max === r) {
				h = 60 * ((g - b) / dm)
				if (g < b) {
					h += 360;
				}
			}
			else if (max === g) {
				h = 60 * ((b - r) / dm) + 120;
			}
			else {
				h = 60 * ((r - g) / dm) + 240;
			}

			let s = max === 0 ? 0 : (1 - (min / max)) * BitColor.MAX_COMPONENT_VALUE;
			let v = max;
			h = Math.clamp(h!, 0, BitColor.MAX_HUE_VALUE);

			return Object.create(null, {
				"hue": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: h
				},
				"saturation": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: s
				},
				"value": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: v
				},
				"alpha": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: this.alpha
				}
			});
		}
		/**
		 * @returns {CYMColor} Представление объекта в CYM (**C**yan, **M**agenta, **Y**ellow) формате
		 */
		toCYM(): CYMColor {
			const r = this.red;
			const g = this.green;
			const b = this.blue;
			const m = BitColor.MAX_COMPONENT_VALUE;

			return Object.create(null, {
				"cyan": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: m - r
				},
				"magenta": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: m - g
				},
				"yellow": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: m - b
				},
				"alpha": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: this.alpha
				}
			});
		}
		toMonochrome(): MonochromeColor {
			return Object.create(null, {
				"value": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: Math.floor((this.red + this.green + this.blue) / 3)
				},
				"alpha": {
					enumerable: true,
					configurable: false,
					writable: true,
					value: this.alpha
				}
			});
		}

		/**
		 * Максимальное значение цветового канола
		 */
		declare static readonly MAX_COMPONENT_VALUE: number;
		/**
		 * Минимальное значение цветового канола
		 */
		declare static readonly MIN_COMPONENT_VALUE: number;
		/**
		 * Минимальное значение оттенка
		 */
		declare static readonly MAX_HUE_VALUE: number;

		/**
		 * Белый цвет в бинарном виде
		 */
		declare static readonly BIN_WHITE: number;
		/**
		 * Чёрный цвет в бинарном виде
		 */
		declare static readonly BIN_BLACK: number;
		/**
		 * Прозрачный цвет в бинарном виде
		 */
		declare static readonly BIN_TRANSPARENT: number;

		/**
		 * Маска красного канала для бинарного представления
		 */
		declare static readonly BIN_RED_MASK: number;
		/**
		 * Маска синего канала для бинарного представления
		 */
		declare static readonly BIN_BLUE_MASK: number;
		/**
		 * Маска зелёного канала для бинарного представления
		 */
		declare static readonly BIN_GREEN_MASK: number;
		/**
		 * Маска канала прозрачности для бинарного представления
		 */
		declare static readonly BIN_ALPA_MASK: number;

		/**
		 * Конвертирует RGB представление в бинарный вид
		 * @param {Partial<RGBColor>} value RGB представление
		 * @returns {number} Бинарное представление
		 */
		static binary(value: Partial<RGBColor>): number {
			const req: RGBColor = {
				alpha: numberToUInt(value.alpha ?? BitColor.MAX_COMPONENT_VALUE) & BitColor.MAX_COMPONENT_VALUE,
				red: numberToUInt(value.red ?? 0) & BitColor.MAX_COMPONENT_VALUE,
				green: numberToUInt(value.green ?? 0) & BitColor.MAX_COMPONENT_VALUE,
				blue: numberToUInt(value.blue ?? 0) & BitColor.MAX_COMPONENT_VALUE,
			};
			return req.red | (req.green << 8) | (req.blue << 16) | (req.alpha << 24);
		}

		/**
		 * Конвертирует RGB представление в цветовой объект
		 * @param {Partial<RGBColor>} value RGB представление для конвертации
		 * @returns {BitColor} цветовой объект
		 */
		static fromRGB(value: Partial<RGBColor>): BitColor {
			const req: RGBColor = {
				alpha: value.alpha ?? BitColor.MAX_COMPONENT_VALUE,
				red: value.red ?? 0,
				green: value.green ?? 0,
				blue: value.blue ?? 0,
			};
			const r = new BitColor(this.binary(req), {
				alpha: req.alpha !== BitColor.MAX_COMPONENT_VALUE
			});

			return r;
		}
		/**
		 * Конвертирует CYM (**C**yan, **M**agenta, **Y**ellow) представление в цветовой объект
		 * @param {Partial<RGBColor>} value CYM (**C**yan, **M**agenta, **Y**ellow) представление для конвертации
		 * @returns {BitColor} цветовой объект
		 */
		static fromCYM(value: Partial<CYMColor>): BitColor {
			const req: CYMColor = {
				alpha: value.alpha ?? BitColor.MAX_COMPONENT_VALUE,
				cyan: value.cyan ?? BitColor.MAX_COMPONENT_VALUE,
				magenta: value.magenta ?? BitColor.MAX_COMPONENT_VALUE,
				yellown: value.yellown ?? BitColor.MAX_COMPONENT_VALUE,
			};
			return this.fromRGB({
				red: BitColor.MAX_COMPONENT_VALUE - req.cyan,
				green: BitColor.MAX_COMPONENT_VALUE - req.magenta,
				blue: BitColor.MAX_COMPONENT_VALUE - req.yellown,
				alpha: req.alpha
			});
		}
		/**
		 * Конвертирует РЫМ представление в цветовой объект
		 * @param {Partial<RGBColor>} value РЫМ представление для конвертации
		 * @returns {BitColor} цветовой объект
		 */
		static fromHSV(value: Partial<HSVColor>): BitColor {
			const req: HSVColor = {
				alpha: value.alpha ?? BitColor.MAX_COMPONENT_VALUE,
				hue: value.hue ?? 0,
				saturation: value.saturation ?? BitColor.MAX_COMPONENT_VALUE,
				value: value.value ?? BitColor.MAX_COMPONENT_VALUE
			}

			const hueIt = Math.floor(req.hue / 60) % 6;
			const vmin = ((
				BitColor.MAX_COMPONENT_VALUE - req.saturation
			) * req.value) / BitColor.MAX_COMPONENT_VALUE;

			const a = (req.value - vmin) * ((req.hue % 60) / 60);

			const vi = vmin + a;
			const vd = req.value - a;

			let r: number, g: number, b: number;

			switch (hueIt) {
				case 0:
					r = req.value;
					g = vi;
					b = vmin;
					break;

				case 1:
					r = vd;
					g = req.value;
					b = vmin;
					break;

				case 2:
					r = vmin;
					g = req.value;
					b = vi;
					break;

				case 3:
					r = vmin;
					g = vd;
					b = req.value;
					break;

				case 4:
					r = vi;
					g = vmin;
					b = req.value;
					break;

				default:
					r = req.value;
					g = vmin;
					b = vd;
					break;
			}

			return BitColor.fromRGB({
				red: r,
				green: g,
				blue: b,
				alpha: req.alpha
			});
		}
		/**
		 * Создаёт цвет из монохромного представления
		 * @param {Partial<MonochromeColor>} value Монохромное представления
		 * @returns {BitColor} Цвет
		 */
		static fromMonochrome(value: Partial<MonochromeColor>): BitColor {
			return new BitColor(this.binary({
				red: value.value, green: value.value, alpha: value.alpha
			}), {
				alpha: value.alpha !== BitColor.MAX_COMPONENT_VALUE
			});
		}

		override isEqual(object: unknown): boolean {
			if (object instanceof BitColor) {
				return this.getSolidBinary() === object.getSolidBinary()
					&& this.alpha == object.alpha;
			}
			return false;
		}

		/**
		 * Генерирует случайный цвет
		 * @param {"RGB"} format Цветовой формат для генерации
		 * @param {?ColorRandomOptionsRGB} options Настройки генерации. Зависит от цветового формата
		 * @returns {BitColor} Случайно сгенрированный цвет
		 */
		static random(format: "RGB", options?: ColorRandomOptionsRGB): BitColor
		/**
		 * Генерирует случайный цвет
		 * @param {"HSV"} format Цветовой формат для генерации
		 * @param {?ColorRandomOptionsHSV} options Настройки генерации. Зависит от цветового формата
		 * @returns {BitColor} Случайно сгенрированный цвет
		 */
		static random(format: "HSV", options?: ColorRandomOptionsHSV): BitColor
		/**
		 * Генерирует случайный цвет
		 * @param {"CYM"} format Цветовой формат для генерации
		 * @param {?ColorRandomOptionsCYM} options Настройки генерации. Зависит от цветового формата
		 * @returns {BitColor} Случайно сгенрированный цвет
		 */
		static random(format: "CYM", options?: ColorRandomOptionsCYM): BitColor
		/**
		 * Генерирует случайный цвет
		 * @param {"Monochrome"} format Цветовой формат для генерации
		 * @param {?ColorRandomOptionsMonochrome} options Настройки генерации. Зависит от цветового формата
		 * @returns {BitColor} Случайно сгенрированный цвет
		 */
		static random(format: "Monochrome", options?: ColorRandomOptionsMonochrome): BitColor
		static random(format: ColorFormat, options: ColorRandomOptions = {}): BitColor {
			let tx: number, ty: number, tz: number, twa: number = 255;
			let cr: ReqRequired<ColorRandomOptions>;
			let result: BitColor;

			function __value(v: number): number {
				return ~~Math.clamp(v, 0, 255);
			}
			function __value_hue(v: number): number {
				return ~~Math.clamp(v, 0, BitColor.MAX_HUE_VALUE);
			}

			switch (format) {
				case "CYM":
					const oCYM: ColorRandomOptionsCYM = options as any;
					const reqCYM: ReqRequired<ColorRandomOptionsCYM> = {
						base: {
							alpha: oCYM.base?.alpha ?? oCYM.alpha?.allow ?? false,
							filter: oCYM.base?.filter ?? null,
							readonly: oCYM.base?.readonly ?? false
						},
						limits: {
							minCyan: __value(oCYM.limits?.minCyan ?? 0),
							maxCyan: __value(oCYM.limits?.maxCyan ?? 255),
							minMagenta: __value(oCYM.limits?.minMagenta ?? 0),
							maxMagenta: __value(oCYM.limits?.maxMagenta ?? 255),
							minYellow: __value(oCYM.limits?.minYellow ?? 0),
							maxYellow: __value(oCYM.limits?.maxYellow ?? 255),
						},
						alpha: {
							allow: oCYM.alpha?.allow ?? false,
							min: 255,
							max: 255,
						}
					};
					if (oCYM.alpha?.allow && reqCYM.alpha.allow) {
						reqCYM.alpha.min = __value(oCYM.alpha.min ?? 0);
						reqCYM.alpha.max = __value(oCYM.alpha.max ?? 255);
					}

					tx = Math.intRandom(reqCYM.limits.minCyan, reqCYM.limits.maxCyan);
					ty = Math.intRandom(reqCYM.limits.minMagenta, reqCYM.limits.maxMagenta);
					tz = Math.intRandom(reqCYM.limits.minYellow, reqCYM.limits.maxYellow);
					if (reqCYM.alpha.allow) {
						twa = Math.intRandom(reqCYM.alpha.min, reqCYM.alpha.max);
					}

					cr = reqCYM;
					result = this.fromCYM({
						alpha: twa,
						cyan: tx,
						magenta: ty,
						yellown: tz
					});
					break;

				case "HSV":
					const oHSV: ColorRandomOptionsHSV = options as any;
					const reqHSV: ReqRequired<ColorRandomOptionsHSV> = {
						base: {
							alpha: oHSV.base?.alpha ?? oHSV.alpha?.allow ?? false,
							filter: oHSV.base?.filter ?? null,
							readonly: oHSV.base?.readonly ?? false
						},
						limits: {
							minHue: __value_hue(oHSV.limits?.minHue ?? 0),
							maxHue: __value_hue(oHSV.limits?.maxHue ?? BitColor.MAX_HUE_VALUE),
							minSaturation: __value(oHSV.limits?.minSaturation ?? 0),
							maxSaturation: __value(oHSV.limits?.maxSaturation ?? 255),
							minValue: __value(oHSV.limits?.minValue ?? 0),
							maxValue: __value(oHSV.limits?.maxValue ?? 255),
						},
						alpha: {
							allow: oHSV.alpha?.allow ?? false,
							min: 255,
							max: 255,
						}
					};
					if (oHSV.alpha?.allow && reqHSV.alpha.allow) {
						reqHSV.alpha.min = __value(oHSV.alpha.min ?? 0);
						reqHSV.alpha.max = __value(oHSV.alpha.max ?? 255);
					}

					tx = Math.limitRandom(reqHSV.limits.maxHue, reqHSV.limits.maxHue);
					ty = Math.limitRandom(reqHSV.limits.maxSaturation, reqHSV.limits.minSaturation);
					tz = Math.limitRandom(reqHSV.limits.minValue, reqHSV.limits.maxValue);
					if (reqHSV.alpha.allow) {
						twa = Math.intRandom(reqHSV.alpha.min, reqHSV.alpha.max);
					}

					cr = reqHSV;
					result = this.fromHSV({
						alpha: twa,
						hue: tx,
						saturation: ty,
						value: tz
					});
					break;

				case "RGB":
					const oRGB: ColorRandomOptionsRGB = options as any;
					const reqRGB: ReqRequired<ColorRandomOptionsRGB> = {
						base: {
							alpha: oRGB.base?.alpha ?? oRGB.alpha?.allow ?? false,
							filter: oRGB.base?.filter ?? null,
							readonly: oRGB.base?.readonly ?? false
						},
						limits: {
							minRed: __value(oRGB.limits?.minRed ?? 0),
							maxRed: __value(oRGB.limits?.maxRed ?? 255),
							minGreen: __value(oRGB.limits?.minGreen ?? 0),
							maxGreen: __value(oRGB.limits?.maxGreen ?? 255),
							minBlue: __value(oRGB.limits?.minBlue ?? 0),
							maxBlue: __value(oRGB.limits?.maxBlue ?? 255),
						},
						alpha: {
							allow: oRGB.alpha?.allow ?? false,
							min: 255,
							max: 255,
						}
					};
					if (oRGB.alpha?.allow && reqRGB.alpha.allow) {
						reqRGB.alpha.min = __value(oRGB.alpha.min ?? 0);
						reqRGB.alpha.max = __value(oRGB.alpha.max ?? 255);
					}

					tx = Math.intRandom(reqRGB.limits.maxRed, reqRGB.limits.maxRed);
					ty = Math.intRandom(reqRGB.limits.maxGreen, reqRGB.limits.minGreen);
					tz = Math.intRandom(reqRGB.limits.minBlue, reqRGB.limits.maxBlue);
					if (reqRGB.alpha.allow) {
						twa = Math.intRandom(reqRGB.alpha.min, reqRGB.alpha.max);
					}

					cr = reqRGB;
					result = this.fromRGB({
						alpha: twa,
						red: tx,
						green: ty,
						blue: tz
					});
					break;

				case "Monochrome":
					const oMono: ColorRandomOptionsMonochrome = options as any;
					const reqMono: ReqRequired<ColorRandomOptionsMonochrome> = {
						base: {
							alpha: oMono.base?.alpha ?? oMono.alpha?.allow ?? false,
							filter: oMono.base?.filter ?? null,
							readonly: oMono.base?.readonly ?? false
						},
						limits: {
							minValue: __value(oMono.limits?.minValue ?? 0),
							maxValue: __value(oMono.limits?.maxValue ?? 255),
						},
						alpha: {
							allow: oMono.alpha?.allow ?? false,
							min: 255,
							max: 255,
						}
					};
					if (oMono.alpha?.allow && reqMono.alpha.allow) {
						reqMono.alpha.min = __value(oMono.alpha.min ?? 0);
						reqMono.alpha.max = __value(oMono.alpha.max ?? 255);
					}

					let tv = Math.intRandom(reqMono.limits.minValue, reqMono.limits.maxValue);
					if (reqMono.alpha.allow) {
						twa = Math.intRandom(reqMono.alpha.min, reqMono.alpha.max);
					}

					cr = reqMono;
					result = this.fromMonochrome({
						value: tv,
						alpha: twa
					});
					break;

				default:
					cr = {
						base: {
							alpha: false,
							filter: null,
							readonly: false
						},
						alpha: {
							allow: false
						},
						limits: {} as any
					}
					result = new BitColor(this.BIN_BLACK);
					break;
			}
			const a = cr.base.alpha;
			if (cr.base.filter) {
				result = (cr.base.filter as ColorFilter)(result, a);
			}

			result.allowAlpha = a;
			if (cr.base.readonly) {
				result.preventChange();
			}

			return result;
		}
	}

	Object.defineProperty(BitColor, "MAX_COMPONENT_VALUE", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 255
	});
	Object.defineProperty(BitColor, "MAX_HUE_VALUE", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 360
	});
	Object.defineProperty(BitColor, "MIN_COMPONENT_VALUE", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 0
	});

	Object.defineProperty(BitColor, "BIN_WHITE", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 0xFFFFFFFF
	});
	Object.defineProperty(BitColor, "BIN_BLACK", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 0x000000FF
	});
	Object.defineProperty(BitColor, "BIN_TRANSPARENT", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 0x00000000
	});

	Object.defineProperty(BitColor, "BIN_RED_MASK", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 0x000000FF
	});
	Object.defineProperty(BitColor, "BIN_GREEN_MASK", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 0x0000FF00
	});
	Object.defineProperty(BitColor, "BIN_BLUE_MASK", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 0x00FF0000
	});
	Object.defineProperty(BitColor, "BIN_ALPA_MASK", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: 0xFF000000
	});
	//#endregion

	//#region Position2D
	/**
	 * Тип компонента позиции
	 * 
	 * ### Возможные значения
	 * 
	 * `"number"` — Полностью соответствует JS типу `number`  
	 * `"float"` — Соответствует JS типу `number`, но не поддерживает бесконечные значения: `Infinity`, `-Infinity`, `NaN`  
	 * `"unsigned"` — Соответствует JS типу `number`, но не поддерживае отричательные ит бесконечные значения: 
	 * `Infinity`, `-Infinity`, `NaN`  
	 * `"integer"` — Поддерживает только цедые цисла  
	 * `"uint"` — Поддерживает только целые беззнаковые числа
	 */
	export type Position2DType = "number" | "float" | "unsigned" | "integer" | "uint";

	export interface Position2DSetOptuions {
		/**
		 * Тип, в который выполнется преобразование X компоненты позиции
		 */
		parseX?: Position2DType;
		/**
		 * Тип, в который выполнется преобразование Y компоненты позиции
		 */
		parseY?: Position2DType;
	}
	export interface Position2DSetCopyOptuions extends Position2DSetOptuions {
		/**
		 * Указывает на наличие ограничения на изменения компонентов данного оъекта
		 */
		readonly?: boolean;
	}
	export interface Position2DConstructorOptuions extends Position2DSetCopyOptuions {
		/**
		 * Опции ограничения значения позиции
		 */
		clamp?: Position2DClampOptions;
	}

	export type Position2DClampOptions = {
		/**
		 * Минимальное значение X-компоненты
		 */
		minX?: number;
		/**
		 * Минимальное значение Y-компоненты
		 */
		minY?: number;
		/**
		 * Тип, к которому будут привелены компоненты позиции
		 */
		type: Position2DType;
	} & ({
		/**
		 * Максимальное значение X-компоненты
		 */
		maxX: number;
	} | {
		/**
		 * Разица между минимальным и максимальным ограничителем X-компоненты
		 */
		width: number;
		minX: number;
	} | {}) & ({
		/**
		 * Максимальное значение Y-компоненты
		 */
		maxY: number;
	} | {
		/**
		 * Разица между минимальным и максимальным ограничителем Y-компоненты
		 */
		height: number;
		minY: number;
	} | {});

	/**
	 * Позиция в двухмерном пространстве
	 * 
	 * ### Слоты
	 * 
	 * | Имя | Наследование  | Назначение           |
	 * |-----|---------------|----------------------|
	 * | `X` | `overridable` | X-компонента позиции |
	 * | `Y` | `overridable` | Y-компонента позиции |
	 */
	@$safedConstructor
	export class Position2D extends BaseWritable {
		/**
		 * Текущая позиция по X
		 */
		get currentX(): number {
			return _GET_SLOT(this, Private.POSITION_2D_OBJECT, "X") ?? NaN;
		}
		set currentX(v: number) {
			if (this.writtable) {
				_SET_SLOT(this, Private.POSITION_2D_OBJECT, "X", Number(v));
			}
		}
		/** 
		 * Текущая позиция по Y
		 */
		get currentY(): number {
			return _GET_SLOT(this, Private.POSITION_2D_OBJECT, "Y") ?? NaN;
		}
		set currentY(v: number) {
			if (this.writtable) {
				_SET_SLOT(this, Private.POSITION_2D_OBJECT, "Y", Number(v));
			}
		}

		/**
		 * Текущая позиция по X. Приведена к типу `"float"`
		 * @see {@link SooperADS.Position2DType}
		 */
		get safeFloatX(): number {
			return isFinite(this.currentX) ? this.currentX : 0;
		}
		set safeFloatX(v: number) {
			if (!isFinite(v)) {
				v = 0;
			}
			this.currentX = v;
		}
		/**
		 * Текущая позиция по Y. Приведена к типу `"float"`
		 * @see {@link SooperADS.Position2DType}
		 */
		get safeFloatY(): number {
			return isFinite(this.currentY) ? this.currentY : 0;
		}
		set safeFloatY(v: number) {
			if (!isFinite(v)) {
				v = 0;
			}
			this.currentY = v;
		}

		/**
		 * Текущая позиция по X. Приведена к типу `"unsigned"`
		 * @see {@link SooperADS.Position2DType}
		 */
		get unsignedFloatX(): number {
			return numberToUnsigned(this.currentX);
		}
		set unsignedFloatX(v: number) {
			this.currentX = numberToUnsigned(v);
		}
		/**
		 * Текущая позиция по X. Приведена к типу `"unsigned"`
		 * @see {@link SooperADS.Position2DType}
		 */
		get unsignedFloatY(): number {
			return numberToUnsigned(this.currentY);
		}
		set unsignedFloatY(v: number) {
			this.currentY = numberToUnsigned(v);
		}

		/**
		 * Текущая позиция по X. Приведена к типу `"integer"`
		 * @see {@link SooperADS.Position2DType}
		 */
		get integerX(): number {
			return numberToInt(this.currentX);
		}
		set integerX(v: number) {
			this.currentX = numberToInt(v);
		}
		/**
		 * Текущая позиция по Y. Приведена к типу `"integer"`
		 * @see {@link SooperADS.Position2DType}
		 */
		get integerY(): number {
			return numberToInt(this.currentY);
		}
		set integerY(v: number) {
			this.currentY = numberToInt(v);
		}

		/**
		 * Текущая позиция по X. Приведена к типу `"uint"`
		 * @see {@link SooperADS.Position2DType}
		 */
		get unsignedIntegerX(): number {
			return numberToUInt(this.currentX);
		}
		set unsignedIntegerX(v: number) {
			this.currentX = numberToUInt(v);
		}
		/**
		 * Текущая позиция по Y. Приведена к типу `"uint"`
		 * @see {@link SooperADS.Position2DType}
		 */
		get unsignedIntegerY(): number {
			return numberToUInt(this.currentY);
		}
		set unsignedIntegerY(v: number) {
			this.currentY = numberToUInt(v);
		}

		/**
		 * Показывает, удалено ли значение позиции
		 * @deprecated
		 */
		declare readonly deprecated: boolean;
		/**
		 * Определяет, действительно ли значение данной позиции
		 * 
		 * >**ПРИМЕЧАНИЕ**: Под действительным значением позиции подразумевается, что все 
		 * компоненты позиции не равны `NaN`
		 */
		declare readonly valid: boolean;

		/**
		 * Тип X- компоненты позиции
		 */
		declare readonly typeX: Position2DType;
		/**
		 * Тип Y- компоненты позиции
		 */
		declare readonly typeY: Position2DType;

		/**
		 * Создаёт объект позиции
		 */
		constructor()
		/**
		 * Создаёт объект позиции
		 * @param {number} all Значение X и Y компонентов
		 */
		constructor(all: number)
		/**
		 * Создаёт объект позиции с указанными параметрами
		 * @param {number} x X-компонента
		 * @param {number} y Y-компонента
		 * @param {Position2DConstructorOptuions} opts Опции
		 */
		constructor(x: number, y: number, opts?: Position2DConstructorOptuions)
		constructor(x?: number, y?: number, opts: Position2DConstructorOptuions = {}) {
			super();

			let cx = x ?? 0;
			let cy = y ?? cx;

			_INIT_SLOT(this, Private.POSITION_2D_OBJECT, "X", Number(cx), false);
			_INIT_SLOT(this, Private.POSITION_2D_OBJECT, "Y", Number(cy), false);

			this.valueTo(opts.parseX, opts.parseY);
			if (opts.clamp) {
				this.clampThis(opts.clamp);
			}

			if (opts.readonly) {
				this.preventChange();
			}

			this.prototypeFreeze();
		}

		/**
		 * Копирует текущию позицию
		 * @param {?boolean} readonly Если не равен `undefined`, указывает, будет ли копия неизменяема
		 * @returns {Position2D} Копия данной позиции
		 */
		copy(readonly?: boolean): Position2D {
			return new Position2D(this.currentX, this.currentY, {
				readonly: readonly ?? !this.writtable
			});
		}

		/**
		 * Удаляет значение текущей позиции
		 * @deprecated
		 */
		delete(): void {
			if (this.writtable) {
				_INIT_SLOT(this, Private.POSITION_2D_OBJECT, "X", NaN, true);
				_INIT_SLOT(this, Private.POSITION_2D_OBJECT, "Y", NaN, true);
				this.preventChange();
			}
		}

		/**
		 * Ограничивает текущию позицию
		 * @param {Position2DClampOptions} copts Параметры огрничения
		 * @returns {boolean} `true` если нет запрета на изменения значений компонент объекта, иначе `false`
		 */
		clampThis(copts: Position2DClampOptions): boolean {
			if (!this.writtable) {
				return false;
			}

			let cx: number = NaN;
			let cy: number = NaN;

			switch (copts.type) {
				case "number":
					cx = this.currentX;
					cy = this.currentY;
					break;
				case "float":
					cx = this.safeFloatX;
					cy = this.safeFloatY;
					break;
				case "integer":
					cx = this.integerX;
					cy = this.integerY;
					break;
				case "unsigned":
					cx = this.unsignedFloatX;
					cy = this.unsignedFloatY;
					break;
				case "uint":
					cx = this.unsignedIntegerX;
					cy = this.unsignedIntegerY;
					break;
			}

			if (copts.minX !== undefined) {
				Math.max(cx, copts.minX);
			}
			if (copts.minY !== undefined) {
				Math.max(cy, copts.minY);
			}

			let maxX: number = Infinity;
			let maxY: number = Infinity;
			if ("maxX" in copts) {
				maxX = copts.maxX ?? Infinity;
			}
			else if ("width" in copts) {
				maxX = copts.minX + copts.width;
			}

			if ("maxY" in copts) {
				maxY = copts.maxY ?? Infinity;
			}
			else if ("height" in copts) {
				maxX = copts.minY + copts.height;
			}

			Math.min(cx, maxX);
			Math.min(cy, maxY);

			switch (copts.type) {
				case "float":
					this.safeFloatX = cx;
					this.safeFloatY = cy;
					break;
				case "integer":
					this.integerX = cx;
					this.integerY = cy;
					break;
				case "unsigned":
					this.unsignedFloatX = cx;
					this.unsignedFloatY = cy;
					break;
				case "uint":
					this.unsignedIntegerX = cx;
					this.unsignedIntegerY = cy;
					break;
				default:
					this.currentX = cx;
					this.currentY = cy;
					break;
			}

			return true;
		}
		/**
		 * Возвращает ограниченную копию текущей позицию
		 * @param {Position2DClampOptions} copts Параметры огрничения
		 * @param {boolean} readonly Будет ли копия неизменяема
		 * @returns {Position2D} Ограниченная копия текущей позицию
		 */
		clamp(copts: Position2DClampOptions, readonly: true): Readonly<Position2D>
		clamp(copts: Position2DClampOptions, readonly?: false): Position2D
		clamp(copts: Position2DClampOptions, readonly: boolean = false): Position2D {
			const r = new Position2D(this.currentX, this.currentY);
			r.clampThis(copts);
			if (readonly) {
				r.preventChange();
			}

			return r;
		}

		override preventChange(): void {
			super.preventChange();
			_INIT_SLOT(this, Private.POSITION_2D_OBJECT, "X", this.currentX, true);
			_INIT_SLOT(this, Private.POSITION_2D_OBJECT, "Y", this.currentY, true);
		}

		/**
		 * Парсет значения компонент до указанных типов. Если тип не указан (или равен `undefined`, 
		 * компонента игнорируется)
		 * @param {Position2DType} x Тип X-компоненты
		 * @param {Position2DType} y Тип Y-компоненты
		 */
		valueTo(x?: Position2DType, y?: Position2DType): void {
			switch (x) {
				case "float":
					this.currentX = this.safeFloatX;
					break;
				case "unsigned":
					this.currentX = this.unsignedFloatX;
					break;
				case "integer":
					this.currentX = this.integerX;
					break;
				case "uint":
					this.currentX = this.unsignedIntegerX
					break;
			}

			switch (y) {
				case "float":
					this.currentY = this.safeFloatY;
					break;
				case "unsigned":
					this.currentY = this.unsignedFloatY;
					break;
				case "integer":
					this.currentY = this.integerY;
					break;
				case "uint":
					this.currentY = this.unsignedIntegerY;
					break;
			}
		}

		/**
		 * Устанавливает значение позиции
		 * @param {number} x X-компонента
		 * @param {number} y Y-компонента
		 * @param {?Position2DSetOptions} opts Опции
		 * @returns {boolean} `true` если нет запрета на изменения значений компонент объекта, иначе `false`
		 */
		set(x: number, y: number, opts?: Position2DSetOptuions): boolean {
			if (!this.writtable) {
				return false;
			}
			this.currentX = x;
			this.currentY = y;

			this.valueTo(opts?.parseX, opts?.parseY);
			return true;
		}

		/**
		 * Добавляет к компонентам текущей позиции заданные значения
		 * @param {number} x X-компонента
		 * @param {number} y Y-компонента
		 * @param {?Position2DSetCopyOptuions} opts Опции
		 * @returns {Position2D} Изменённое значение позиции
		 */
		add(x: number, y: number, opts?: Position2DSetCopyOptuions): Position2D {
			const result = this.copy(false);
			result.addToThis(x, y, opts);
			if (opts?.readonly ?? !this.writtable) {
				result.preventChange();
			}
			return result;
		}
		/**
		 * Добавляет к компонентам текущей позиции заданные значения
		 * @param {number} x X-компонента
		 * @param {number} y Y-компонента
		 * @param {?Position2DSetCopyOptuions} opts Опции
		 * @returns {boolean} `true` если нет запрета на изменения значений компонент объекта, иначе `false`
		 */
		addToThis(x: number, y: number, opts?: Position2DSetOptuions): boolean {
			return this.set(this.safeFloatX + x, this.safeFloatY + y, opts);
		}
		/**
		 * Вычетвет у компонентов текущей позиции заданные значения
		 * @param {number} x X-компонента
		 * @param {number} y Y-компонента
		 * @param {?Position2DSetCopyOptuions} opts Опции
		 * @returns {Position2D} Изменённое значение позиции
		 */
		substract(x: number, y: number, opts: Position2DSetCopyOptuions): Position2D {
			const result = this.copy(false);
			result.addToThis(x, y, opts);
			if (opts?.readonly ?? !this.writtable) {
				result.preventChange();
			}
			return result;
		}
		/**
		 * Вычетвет у компонентов текущей позиции заданные значения
		 * @param {number} x X-компонента
		 * @param {number} y Y-компонента
		 * @param {?Position2DSetCopyOptuions} opts Опции
		 * @returns {boolean} `true` если нет запрета на изменения значений компонент объекта, иначе `false`
		 */
		substructFromThis(x: number, y: number, opts: Position2DSetOptuions): boolean {
			return this.set(this.safeFloatX - x, this.safeFloatY - y, opts);
		}

		/**
		 * @returns {Readonly<Position2D>} Копию данного объекта, с запретом на изменения значений его компонентов
		 */
		toReadonly(): Readonly<Position2D> {
			return new Position2D(this.currentX, this.currentY);
		}
		override toString(): string {
			let pref = "";
			if (this.valid) {
				pref += "valid ";
			}

			return pref + `${this.currentX}[${this.typeX}]; ${this.currentY}[${this.typeY}]`;
		}

		override isEqual(object: unknown): boolean {
			if (object instanceof Position2D) {
				return this.currentX === object.currentX
					&& this.currentY === object.currentY;
			}
			return false;
		}
	}

	Position2D.defineProperty("deprecated", {
		get: function (this: Position2D): boolean {
			const dx = Object.getOwnPropertyDescriptor(this, Private.POSITION_2D_OBJECT.at("X"));
			const dy = Object.getOwnPropertyDescriptor(this, Private.POSITION_2D_OBJECT.at("Y"));

			return (!dx?.configurable && !dy?.configurable);
		}
	});
	Position2D.defineProperty("valid", {
		get: function (this: Position2D): boolean {
			return isFinite(this.currentX) && isFinite(this.currentY);
		}
	});
	Position2D.defineProperty("typeX", {
		get: function (this: Position2D): Position2DType {
			return isFinite(this.currentX)
				? Number.isInteger(this.currentX)
					? this.currentX >= 0
						? "uint"
						: "integer"
					: this.currentX < 0
						? "float"
						: "unsigned"
				: "number";
		}
	});
	Position2D.defineProperty("typeY", {
		get: function (this: Position2D): Position2DType {
			return isFinite(this.currentY)
				? Number.isInteger(this.currentY)
					? this.currentY >= 0
						? "uint"
						: "integer"
					: this.currentY < 0
						? "float"
						: "unsigned"
				: "number";
		}
	});
	//#endregion

	//#region Реестр
	/**
	 * Карта событий для класса `Registry`
	 * 
	 * @see {@link SooperADS.Registry}
	 */
	export type RegistryEventMap<T> = {
		/**
		 * Событие обновления реестра
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"update": RegistryEvent<T>;
		/**
		 * Класс событий, отвевтственный за изменения `SooperADS.Registry.prototype.globalAttributes`
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"attributes": RegistryEvent<T>;
		/**
		 * Событие изменения значений аттрибутов
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"attributes.change": RegistryAttributesChangeEvent<T>;
		/**
		 * Вызывается, после заморозки значений аттрибутов
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"attributes.freeze": RegistryEvent<T>;
		/**
		 * Класс событий, ответственный за операции на записями в реестре
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"note": RegistryNoteEvent<T>;
		/**
		 * Событие удаления записи
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"note.delete": RegistryNoteEvent<T>;
		/**
		 * События изменения значения записи
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |  
		 */
		"note.change/value": RegistryNoteChangeValueEvent<T>;
		/**
		 * Событие изменения конфигурации записи
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		*/
		"note.change/attributes": RegistryNoteChangeAttributesEvent<T>;
		/**
		 * Событие создания записи
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"note.create": RegistryNoteCreateEvent<T>;
	}

	/**
	 * Базовый клпсс для всех событий, относящихся к `SooperADS.Registry`
	 * 
	 * @see {@link SooperADS.RegistryEventMap}
	 */
	@$safedConstructor
	export class RegistryEvent<T> extends Event {
		/**
		 * Цель события
		 */
		declare readonly target: Registry<T>;
	}

	/**
	 * Событие изменения значений аттрибутов
	 */
	@$safedConstructor
	export class RegistryAttributesChangeEvent<T> extends RegistryEvent<T> {
		/**
		 * Текущая конфигкрация реестра
		 */
		declare readonly currentAttributes: Readonly<RegistryAttributes>;
		/**
		 * Старая конфигкрация реестра
		 */
		declare readonly oldAttributes: Readonly<RegistryAttributes>;

		/**
		 * @returns {(keyof RegistryAttributes)[]} Списрк изменённых атрибутов реестра
		 */
		changeattributes(): (keyof RegistryAttributes)[] {
			let r: (keyof RegistryAttributes)[] = [];
			for (const key in this.currentAttributes) {
				const v1 = this.currentAttributes[key as keyof RegistryAttributes];
				const v2 = this.oldAttributes[key as keyof RegistryAttributes];

				if (v1 !== v2) {
					r.push(key as keyof RegistryAttributes);
				}
			}

			return r;
		}

		/**
		 * @param {Registry<T>} target Цель события
		 * @param {EventFullName} name Полное имя события
		 * @param {Readonly<RegistryAttributes>} old Старая конфигурация
		 */
		constructor(target: Registry<T>, name: EventFullName, old: Readonly<RegistryAttributes>) {
			super(target, name)

			Object.defineProperties(this, {
				"oldAttributes": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: old
				},
				"currentAttributes": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: target.globalAttributes
				}
			});
		}
	}
	/**
	 * Класс событий, ответственный за операции на записями в реестре
	 */
	@$safedConstructor
	export class RegistryNoteEvent<T> extends RegistryEvent<T> {
		/**
		 * Имя записи
		 */
		declare readonly targetNoteKey: PropertyKey;

		/**
		 * Проверяет, существует ли запись
		 */
		targetNoteExist(): boolean {
			return this.target.hasNote(this.targetNoteKey);
		}

		/**
		 * @param {Registry<T>} target Цель события
		 * @param {EventFullName} name Полное имя события
		 * @param {PropertyKey} key *Имя* записи
		 */
		constructor(target: Registry<T>, name: EventFullName, key: PropertyKey) {
			super(target, name)

			Object.defineProperty(this, "targetNoteKey", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: key
			});
		}
	}
	/**
	 * События изменения значения записи
	 */
	@$safedConstructor
	export class RegistryNoteChangeValueEvent<T> extends RegistryNoteEvent<T> {
		/**
		 * Текущее значение записи
		 */
		declare readonly targetNoteValue: T;
		/**
		 * Старое значение записи
		 */
		declare readonly oldValue: T;

		/**
		 * @param {Registry<T>} target Цель события
		 * @param {EventFullName} name Полное имя события
		 * @param {PropertyKey} key *Имя* записи
		 * @param {T} v Текущее значение записи [**ОПТИМИЗАЦИЯ**]
		 * @param {T} oldv Старое значение записи
		 */
		constructor(target: Registry<T>, name: EventFullName, key: PropertyKey, v: T, oldv: T) {
			super(target, name, key)

			Object.defineProperty(this, "targetNoteValue", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: v
			});
			Object.defineProperty(this, "oldValue", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: oldv
			});
		}
	}
	/**
	 * Событие изменения конфигурации записи
	 */
	@$safedConstructor
	export class RegistryNoteChangeAttributesEvent<T> extends RegistryNoteEvent<T> {
		/**
		 * Текущая конфигурация записи
		 */
		declare readonly targetNoteAttributes: RegistryNoteAttributes;
		/**
		 * Старая конфигурация записи
		 */
		declare readonly oldAttributes: RegistryNoteAttributes;

		/**
		 * @param {Registry<T>} target Цель события
		 * @param {EventFullName} name Полное имя события
		 * @param {PropertyKey} key *Имя* записи
		 * @param {RegistryNoteAttributes} attributes Текущея конфигурация записи [**ОПТИМИЗАЦИЯ**]
		 * @param {RegistryNoteAttributes} old Старая конфигурация записи
		 */
		constructor(
			target: Registry<T>,
			name: EventFullName,
			key: PropertyKey,
			attributes: RegistryNoteAttributes,
			old: RegistryNoteAttributes
		) {
			super(target, name, key)

			Object.defineProperty(this, "targetNoteAttributes", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: attributes
			});
			Object.defineProperty(this, "oldAttributes", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: old
			});
		}
	}
	/**
	 * Событие создания записи 
	 */
	@$safedConstructor
	export class RegistryNoteCreateEvent<T> extends RegistryNoteEvent<T> {
		/**
		 * Текущая конфигурация записи
		 */
		declare readonly targetNoteValue: T;
		/**
		 * Текущее значение записи
		 */
		declare readonly targetNoteAttributes: RegistryNoteAttributes;

		/**
		 * @param {Registry<T>} target Цель события
		 * @param {EventFullName} name Полное имя события
		 * @param {PropertyKey} key *Имя* записи
		 * @param {T} value Текущее значение записи [**ОПТИМИЗАЦИЯ**]
		 * @param {RegistryNoteAttributes} attrs Текущея конфигурация записи [**ОПТИМИЗАЦИЯ**]
		 */
		constructor(
			target: Registry<T>,
			name: EventFullName,
			key: PropertyKey,
			value: T,
			attrs: RegistryNoteAttributes
		) {
			super(target, name, key)

			Object.defineProperties(this, {
				"targetNoteValue": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: value
				},
				"targetNoteAttributes": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: attrs
				},
			});
		}
	}

	/**
	 * Конфигурация реестра
	 */
	export interface RegistryAttributes {
		/**
		 * Определяет, разрешено ли удалять записи
		 */
		notesRemoveble: boolean;
		/**
		 * Определяет, разрешено ли переписывать значение записи
		 */
		notesRewritable: boolean;
		/**
		// * Определяет, разрешено ли менять конфигурацию записи
		 */
		notesConfigrable: boolean;
		/**
		 * Определяет, разрешено ли создавать записи
		 */
		notesCreate: boolean;
		/**
		 * Определяет, заморожены ли атрибуты реестра. Если атрибуты реестра 
		 * заморожены, их значения нельзя изменить
		 */
		attributesFreeze: boolean;
	}

	/**
	 * Хранилище записей реестра
	 */
	export interface RegistryNotesStorage<T> {
		[v: PropertyKey]: RegistryNote<T>;
	}

	/**
	 * Конфигурация записи реестра
	 */
	export interface RegistryNoteAttributes {
		/**
		 * Определяет, можно ли удалить данную запись
		 */
		removeble: boolean;
		/**
		 * Определяет, можно ли изменить значение записи
		 */
		writable: boolean;
		/**
		 * Определяет, скрыта ли запись
		 */
		hidden: boolean;
		/**
		 * Определяет, можно ли изменить конфигурацию записи
		 */
		configrable: boolean;
	}
	/**
	 * Запись реестра
	 */
	export interface RegistryNote<T> extends RegistryNoteAttributes {
		/**
		 * Значение
		 */
		value: T;
	}

	/**
	 * Итератор реестра
	 */
	export interface RegistryIterator<T> {
		/**
		 * *Имя* записи
		 */
		key: PropertyKey;
		/**
		 * Значение записи
		 */
		value: T;
		/**
		 * Определяет, скрыта ли запись
		 */
		hidden: boolean;
	}

	/**
	 * Реестр
	 * @see {@link SooperADS.RegistryEventMap}
	 * 
	 * ### Слоты
	 * 
	 * | Имя          | Наследование  | Назначение                      |
	 * |--------------|---------------|---------------------------------|
	 * | `Data`       | `overridable` | Хрпнилище записей реестра       |
	 * | `Attributes` | `always`      | Определяет конфигурацию реестра |
	 */
	@$safedConstructor
	export class Registry<T> extends EventTarget<RegistryEventMap<T>> {
		/**
		 * Конфигурация реестра
		 * 
		 * @see {@link RegistryAttributes}
		 */
		declare readonly globalAttributes: Readonly<RegistryAttributes>;

		/**
		 * Список *имён* записей, назодящихся в данном реестре. Не учитывает скрытые записи
		 */
		declare readonly definedNotesKeys: PropertyKey[];
		/**
		 * Количество записей, назодящихся в данном реестре. Не учитывает скрытые записи
		 */
		declare readonly definedNotesCount: number;

		/**
		 * Создаёт объект реестра
		 * @param {?Partial<RegistryAttributes>} attrs Конфигурация реестра
		 * @param {?RegistryNotesStorage<T>} defaultStorage Записи в реестре [**ЭКСПЕРЕМЕНТАЛЬНО**]
		 */
		constructor(attrs: Partial<RegistryAttributes> = {}, defaultStorage: RegistryNotesStorage<T> = {}) {
			super(true);

			this.defineEvent("update", true, false, null);
			this.defineEvent("attributes", true, false, null);
			this.defineEvent("attributes.change", true, false, null);
			this.defineEvent("attributes.freeze", true, false, null);
			this.defineEvent("note", true, false, null);
			this.defineEvent("note.delete", true, false, null);
			this.defineEvent("note.create", true, false, null);
			this.defineEvent("note.change/value", true, false, null);
			this.defineEvent("note.change/attributes", true, false, null);

			Object.setPrototypeOf(defaultStorage, null);

			_INIT_SLOT(this, Private.REGISTRY_OBJECT, "Data", defaultStorage, true);
			_INIT_SLOT(this, Private.REGISTRY_OBJECT, "Attributes", Object.create(null), false);
			this.setRegistryAttributes(attrs);
		}

		/**
		 * Задаёт значение записи или, если она не определена, определяет её с заданным значением в реестре
		 * @param {PropertyKey} key *Имя записи*
		 * @param {T} value Значение
		 * @returns {boolean} `true` если удалось изменить или определить запись, иначе `false`
		 * 
		 * @see Определение новой записи в реестре {@link SooperADS.Registry.prototype.appendValue}
		 * @see Изменение сущестующей записи в реестре {@link SooperADS.Registry.prototype.setNoteAttributes}
		 */
		writeValue(key: PropertyKey, value: T): boolean {
			if (this.hasNote(key)) {
				return this.setNoteValue(key, value);
			}

			return this.appendValue(key, value);
		}
		/**
		 * Определяет запись с заданным значением и конфигурацией в реестре
		 * @param {PropertyKey} key *Имя записи*
		 * @param {T} value Значение
		 * @param {?Partial<RegistryAttributes>} attributes Конфигурация
		 * @returns {boolean} `true` если удалось определить новую запись, иначе `false`
		 */
		appendValue(key: PropertyKey, value: T, attributes: Partial<RegistryNoteAttributes> = {}): boolean {
			if (!this.hasNote(key)) {
				if (!this.globalAttributes.notesCreate) {
					return false;
				}

				const cattrs = Registry.fullAttributes(attributes);
				const note: RegistryNote<T> = {
					...cattrs,
					value: value
				};
				Object.setPrototypeOf(note, null);
				Object.defineProperty(_GET_SLOT(this, Private.REGISTRY_OBJECT, "Data")!, key, {
					configurable: true,
					writable: true,
					enumerable: !cattrs.hidden,
					value: note
				});

				new RegistryNoteCreateEvent(this, "note.create+update", key, value, cattrs).dispatch();
				return true;
			}
			return false;
		}
		/**
		 * Проверяет, определена ли запись с заданным *именем* в реестре
		 * @param {PropertyKey} key *Имя* записи
		 * @returns {boolean} `true` если событие определено, иначе `false`
		 */
		hasNote(key: PropertyKey): boolean {
			return Object.hasOwn(_GET_SLOT<object>(this, Private.REGISTRY_OBJECT, "Data")!, key)
		}
		/**
		 * Удаляет запись с заданным *именем*
		 * @param {PropertyKey} key *Имя* записи
		 * @returns {boolean} `true` если запись получилось удалить, иначе `false`
		 */
		deleteNote(key: PropertyKey): boolean {
			if (!this.globalAttributes.notesRemoveble) {
				return false;
			}

			const s = _GET_SLOT<RegistryNotesStorage<T>>(this, Private.REGISTRY_OBJECT, "Data")!;
			if (s[key]) {
				if (s[key]!.removeble) {
					delete s[key];
					new RegistryNoteEvent(this, "note.delete+update", key).dispatch();
					return true;
				}
			}
			return false;
		}

		/**
		 * Задаёт значение записи
		 * @param {PropertyKey} key *Имя записи*
		 * @param {T} value Новое значение
		 * @returns {boolean} `true` если значение была изменена, иначе `false`
		 */
		setNoteValue(key: PropertyKey, value: T): boolean {
			let note = _GET_SLOT<RegistryNotesStorage<T>>(this, Private.REGISTRY_OBJECT, "Data")?.[key];
			if (!note) {
				return false;
			}

			if (!this.globalAttributes.notesRewritable || !note.writable) {
				return false;
			}
			const old = note.value;
			note.value = value;

			new RegistryNoteChangeValueEvent(this, "note.change/value+update", key, value, old).dispatch();
			return true;
		}
		/**
		 * Получает значение записи
		 * @param {PropertyKey} key *Имя записи*
		 * @returns {T | undefined} `undefined` если запись не определена в реестре, иначе значение записи
		 */
		getNoteValue(key: PropertyKey): T | undefined {
			return _GET_SLOT<RegistryNotesStorage<T>>(this, Private.REGISTRY_OBJECT, "Data")?.[key]?.value;
		}

		/**
		 * Задаёт конфигурацию записи
		 * @param {PropertyKey} key *Имя записи*
		 * @param {Partial<RegistryAttributes>} attributes Новая конфигурация
		 * @returns {boolean} `true` если конфигурация была изменена, иначе `false`
		 */
		setNoteAttributes(key: PropertyKey, attributes: Partial<RegistryNoteAttributes>): boolean {
			const note = _GET_SLOT<RegistryNotesStorage<T>>(this, Private.REGISTRY_OBJECT, "Data")![key];
			if (!this.globalAttributes.notesConfigrable || !note) {
				return false;
			}

			if (!note.configrable) {
				return false;
			}

			const old: RegistryNoteAttributes = { ...note };
			note.configrable = attributes.configrable ?? note.configrable;
			note.removeble = attributes.removeble ?? note.removeble;
			note.writable = attributes.writable ?? note.writable;
			note.hidden = attributes.hidden ?? note.hidden;
			const cur: RegistryNoteAttributes = { ...note };

			Reflect.deleteProperty(old, "value");
			Reflect.deleteProperty(cur, "value");
			Object.setPrototypeOf(old, null);
			Object.setPrototypeOf(cur, null);

			Object.defineProperty(_GET_SLOT(this, Private.REGISTRY_OBJECT, "Data")!, key, {
				configurable: true,
				writable: true,
				enumerable: !note.hidden,
				value: note
			});

			new RegistryNoteChangeAttributesEvent(this, "note.change/attributes+update", key, cur, old).dispatch();
			return true;
		}
		/**
		 * Получает конфигурацию записи
		 * @param {PropertyKey} key *Имя записи*
		 * @returns {RegistryNoteAttribute | undefined} `undefined` если запись не определена в реестре, 
		 * иначе копию конфигурации записи
		 */
		getNoteAttributes(key: PropertyKey): RegistryNoteAttributes | undefined {
			const note = _GET_SLOT<RegistryNotesStorage<T>>(this, Private.REGISTRY_OBJECT, "Data")![key];
			if (note) {
				return {
					configrable: note.configrable,
					hidden: note.hidden,
					removeble: note.removeble,
					writable: note.writable
				};
			}
			return undefined;
		}

		/**
		 * Задаёт конфигурацию реестра
		 * @param {Partial<RegistryAttributes>} attrs Новая конфигурация
		 * @returns {boolean} `true` если конфигурация не была заморожена, иначе `false`
		 */
		setRegistryAttributes(attrs: Partial<RegistryAttributes>): boolean {
			if (this.globalAttributes?.attributesFreeze) {
				return false;
			}

			let old = this.globalAttributes;
			_SET_SLOT(this, Private.REGISTRY_OBJECT, "Attributes", Object.create(null, {
				"attributesFreeze": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: attrs.attributesFreeze ?? false
				},
				"notesCreate": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: attrs.notesCreate ?? true
				},
				"notesRemoveble": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: attrs.notesRemoveble ?? true
				},
				"notesRewritable": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: attrs.notesRewritable ?? true
				},
				"notesConfigrable": {
					configurable: false,
					enumerable: true,
					writable: false,
					value: attrs.notesConfigrable ?? true
				},
			}));

			if (this.globalAttributes.attributesFreeze) {
				new RegistryEvent<T>(this, "attributes.freeze+update").dispatch();
			}
			let change = new RegistryAttributesChangeEvent<T>(this, "attributes.change+update", old);
			if (change.changeattributes().length) {
				change.dispatch();
			}

			return true;
		}

		/**
		 * Заперещает изменение реестра (включая удаление/добавление/изменение записей)
		 */
		preventChange(): void {
			this.setRegistryAttributes({
				attributesFreeze: true,
				notesConfigrable: false,
				notesRewritable: false,
				notesCreate: false,
				notesRemoveble: false
			});
		}

		[Symbol.iterator](): Iterator<RegistryIterator<T>> {
			const storage = _GET_SLOT<RegistryNotesStorage<T>>(this, Private.REGISTRY_OBJECT, "Data")!;
			const names = Reflect.ownKeys(storage);

			let it: number = 0;

			function __pitch_note(): RegistryIterator<T> | undefined {
				let key = names[it];
				while (key) {
					const note = storage[key];
					if (note) {
						return {
							hidden: note.hidden,
							value: note.value,
							key: key
						};
					}

					it++;
					key = names[it];
				}

				return undefined;
			}

			return {
				next: function next() {
					const itr = __pitch_note();
					if (!itr) {
						return { done: true, value: undefined };
					}

					Object.setPrototypeOf(itr, null);
					return { done: false, value: itr };
				}
			}
		}

		static fullAttributes(attributes: Partial<RegistryNoteAttributes> = {}): RegistryNoteAttributes {
			return {
				configrable: attributes.configrable ?? true,
				hidden: attributes.hidden ?? false,
				removeble: attributes.removeble ?? true,
				writable: attributes.writable ?? true
			}
		}
	}
	Registry.defineProperty("globalAttributes", {
		get: function getAttributes<T>(this: Registry<T>): Readonly<RegistryAttributes> {
			return _GET_SLOT(this, Private.REGISTRY_OBJECT, "Attributes") as any;
		},
	})
	Registry.defineProperty("definedNotesKeys", {
		get: function getKeys<T>(this: Registry<T>): PropertyKey[] {
			const r: PropertyKey[] = [];
			const storage = _GET_SLOT<object>(this, Private.REGISTRY_OBJECT, "Data")!;
			for (const key in storage) {
				r.push(key);
			}
			return r;
		},
	})
	Registry.defineProperty("definedNotesCount", {
		get: function getKeysCount<T>(this: Registry<T>): number {
			return this.definedNotesKeys.length;
		},
	})
	//#endregion
	//#endregion

	//#region Расширение событий
	//#region BubbledEventTarget
	/**
	 * Базовый классы для объектов, которые должны поддерживать всплывающие события
	 * 
	 * ### Слоты
	 * 
	 * | Имя             | Наследование  | Назначение                              |
	 * |-----------------|---------------|-----------------------------------------|
	 * | `BubbledParent` | `overridable` | Определяет, куда будут всплывть события |
	 */
	@$safedConstructor
	export class BubbledEventTarget<EventOn extends EventBaseMap = {}> extends EventTarget<EventOn> {
		/**
		 * Конструирует объет цели события
		 * @param {boolean} activeEventsDispatch Будут ли сейчас обрабвтаться события
		 * @param {BubbledEventTarget | null} bubbledParent Родитель лбъекта
		 */
		constructor(activeEventsDispatch: boolean, bubbledParent: BubbledEventTarget | null) {
			super(activeEventsDispatch);

			if (!(bubbledParent instanceof BubbledEventTarget)) {
				bubbledParent = null;
			}
			_INIT_SLOT(this, Private.BUBBLED_EVENT_TARGET_OBJECT, "BubbledParent", bubbledParent, false);
		}

		/**
		 * Выполняет обработку события. НЕ ПРОВЕРЯЕТ ПАРАМЕТРЫ
		 * @param {EventT} event Событие для обработки
		 * @param {EventSubName[]} names *Дополнительные имена* события
		 */
		override[Private.DispatchEvent](event: Event<unknown>, names: EventSubName[]): void {
			names.forEach(evon => {
				this[Private.GetEvent]<Event<unknown>, unknown>(evon)?.dispatch(event);

				if (event instanceof BubbledEvent<unknown>) {
					let target = this.parent;
					while (target && event.propagationAllow) {
						target[Private.GetEvent]<Event<unknown>, unknown>(evon)?.dispatch(event);
						target = target.parent;
					}
				}
			});
		}

		/**
		 * Родитель данного обхета
		 */
		declare readonly parent: BubbledEventTarget<EventOn> | null;
	}
	BubbledEventTarget.defineProperty("parent", {
		get: function getBubbledParent<EventOn extends EventBaseMap>(
			this: BubbledEventTarget<EventOn>
		): BubbledEventTarget<EventOn> | null {
			return _GET_SLOT(this, Private.BUBBLED_EVENT_TARGET_OBJECT, "BubbledParent") ?? null;
		}
	});
	//#endregion
	//#region BubbledEvent
	/**
	 * Всплывающие событие
	 * 
	 * ### Слоты
	 * 
	 * | Имя            | Наследование  | Назначение                             |
	 * |----------------|---------------|----------------------------------------|
	 * | `BubbledAllow` | `overridable` | Определяет, будет ли событие всплывать |
	 */
	@$safedConstructor
	export class BubbledEvent<Result = undefined> extends Event<Result> {
		declare readonly target: BubbledEventTarget;

		/**
		 * Определяет, разрешено ли всплытие события
		 */
		declare readonly propagationAllow: boolean;

		/**
		 * Создаёт объект SooperADS.Event
		 * @param {EventTarget} target Цель события
		 * @param {EventFullName} name Полное имя события
		 * 
		 * > **ВАЖНО**: Полное имя события может содержать **ТОЛКО** символы англиского алфавита 
		 * в произвольном регистре, цифры, '-', '_' и следующие спец. символы: `.`, `+`, `/`, `^` и `:`
		 * 
		 * @see подробнее см. {@link SooperADS.Event.prototype.name SooperADS.Event@name}
		 */
		constructor(target: BubbledEventTarget, name: EventFullName) {
			super(target, name);

			_INIT_SLOT(this, Private.BUBBLED_EVENT_OBJECT, "BubbledAllow", true, false);
		}

		/**
		 * Запрещает всплытие события
		 */
		stopEventPropagation(): void {
			_INIT_SLOT(this, Private.BUBBLED_EVENT_OBJECT, "BubbledAllow", false, true);
		}
	}

	BubbledEvent.defineProperty("propagationAllow", {
		get: function detBubbledAllowState(this: BubbledEvent): boolean {
			return _GET_SLOT(this, Private.BUBBLED_EVENT_OBJECT, "BubbledAllow")!;
		}
	});
	//#endregion
	//#endregion

	//#region CustomElement
	/**
	 * Опции создания контролера пользовательского элемента
	 */
	export interface CustomElementControlerOptions {
		/**
		 * Контролер родительского элемента
		 */
		parentControler?: CustomElementControler;
	}
	/**
	 * Опции конструктора пользовательского элемента
	 */
	export interface CustomElementOptions extends CustomElementControlerOptions {
		controlerActiveEventDispatch?: boolean;
		controlerConstructor?: typeof CustomElementControler;
	}

	Object.defineProperty(Private, "TempPreventCallbacks", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: Slot(null, "TempPreventCallbacks")
	});

	/**
	 * Класс пользовательског элемента
	 */
	export class CustomElement extends HTMLElement {
		/**
		 * Контролер объекта
		 */
		declare readonly ownerControler: CustomElementControler;
		/**
		 * Разрешены ли события элемента для контролера
		 */
		declare readonly allowControlElementEvents: boolean;

		/**
		 * Если равен `true`, то события *html life-cycle* колбеков не будут обработаны и сгенерированы
		 */
		[Private.TempPreventCallbacks]: boolean;

		/**
		 * Создаёт пользовательский HTML элемент
		 * @param {CustomElementOptions} options Опции создания
		 */
		constructor(options: CustomElementOptions = {}) {
			super();

			this[Private.TempPreventCallbacks] = false;
			const constructor = options.controlerConstructor ?? CustomElementControler;
			new constructor(options.controlerActiveEventDispatch ?? true, this, options);
		}

		/**
		 * Вызывается при добавлении в документ
		 */
		protected connectedCallback(): void {
			if (this.allowControlElementEvents) {
				new CustomElementEvent(this.ownerControler, "element.attach").dispatch();
			}
		}
		/**
		 * Вызывается при удалении из документа
		 */
		protected disconnectedCallback(): void {
			if (this.allowControlElementEvents) {
				new CustomElementEvent(this.ownerControler, "element.detach").dispatch();
			}
		}
		/**
		 * Вызывается, когда элемент перемещается в новый документ
		 */
		protected adoptedCallback(): void {
			if (this.allowControlElementEvents) {
				new CustomElementEvent(this.ownerControler, "element.adopted").dispatch();
			}
		}

		/**
		 * Вычисляет правильное значение атрибута
		 * @param {string} name Имя атрибута
		 * @param {string | null} value Текущие значение
		 * @returns {string | null} Правильное значение
		 */
		protected getValidAttribute(name: string, value: string | null): string | null {
			name;
			return value;
		}
		/**
		 * Вызывается когда надо обновить данные текущего объекта, вследствии изменения отслеживаемого атрибута
		 * @param {string} name Имя атрибута
		 * @param {string | null} value Его правильное значение
		 */
		protected applyAttribute(name: string, value: string | null): void {
			name; value;
		}

		/**
		 * Вызывается при изменени одного из отслеживаемых атрибутов
		 * @param {string} name Имя атрибута
		 * @param {string} oldValue Старое значение
		 * @param {string} newValue Новое значение
		 */
		protected attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {
			if (this.allowControlElementEvents) {
				let fixValue = this.getValidAttribute(name, newValue);
				if (fixValue === oldValue && newValue === fixValue) {
					return;
				}

				let evName: keyof CustomElementEventMap = "element.attribute.change";
				if (!oldValue) {
					evName = "element.attribute.create"
				}
				else if (!fixValue) {
					evName = "element.attribute.delete"
				}


				new CustomElementAttributeEvent(
					this.ownerControler, evName, name, oldValue, fixValue, newValue == fixValue
				).dispatch();

				if (fixValue !== newValue) {
					if (!fixValue) {
						this.quietRemoveAttribute(name);
					}
					else {
						this.quietSetAttribute(name, fixValue);
					}
				}
				this.applyAttribute(name, newValue)
			}
		}

		/**
		 * Устанавливет значение заданному атрибуту без генерации событий `element.attribute` 
		 * @param {string} qualifiedName Имя атрибута
		 * @param {string} value Значение
		 */
		quietSetAttribute(qualifiedName: string, value: string): void {
			let old = this[Private.TempPreventCallbacks];
			this[Private.TempPreventCallbacks] = true;

			this.setAttribute(qualifiedName, value)

			this[Private.TempPreventCallbacks] = old;
		}
		/**
		 * Удаляет заданный атрибуту без генерации событий `element.attribute` 
		 * @param {string} qualifiedName Имя атрибута
		 */
		quietRemoveAttribute(qualifiedName: string): void {
			let old = this[Private.TempPreventCallbacks];
			this[Private.TempPreventCallbacks] = true;

			this.removeAttribute(qualifiedName);

			this[Private.TempPreventCallbacks] = old;
		}
		/**
		 * Удаляет заданный атрибуту, если он существует, наче создаёт его. Не генерирует события `element.attribute` 
		 * @param {string} qualifiedName Имя атрибута
		 * @param {?boolean | undefined} force Если не указан или равен `undefined`, атрибут 
		 * с заданным именем удалится если он существует, иначе создастся, если он не существует,
		 * иначе если он равен `true`, атрибут попытается создастся, иначе если равен `false`, атрибут
		 * ппопытается удалится
		 * @returns {boolean} `true` если атрибут в конечном итоге будет присутствовать, иначе `false`
		 */
		quietToggleAttribute(qualifiedName: string, force?: boolean | undefined): boolean {
			let old = this[Private.TempPreventCallbacks];
			this[Private.TempPreventCallbacks] = true;

			let r = this.toggleAttribute(qualifiedName, force);

			this[Private.TempPreventCallbacks] = old;
			return r;
		}

		/**
		 * Массив имён атрибутов для отслеживания 
		 */
		protected declare static readonly observedAttributes: string[];
	}
	/**
	 * Конструктор пользовательского элемента
	 */
	export type CustomElementConstructor = CustomElement;
	Object.defineProperty(CustomElement, "observedAttributes", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: []
	});
	Object.defineProperty(CustomElement.prototype, "allowControlElementEvents", {
		configurable: false,
		enumerable: true,
		get: function getAllow(this: CustomElement): boolean {
			return !this[Private.TempPreventCallbacks];
		}
	});

	Object.defineProperty(Private, "UpdateParent", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: Slot(null, "UpdateParent")
	});

	/**
	 * Событие контролера пользовательского элемента
	 */
	export class CustomElementEvent<Result = undefined> extends BubbledEvent<Result> {
		/**
		 * Целевой HTML элемент
		 */
		declare readonly targetElement: CustomElement;

		declare readonly target: CustomElementControler;

		/**
		 * Создаёт объект SooperADS.Event
		 * @param {CustomElementControler} target Цель события
		 * @param {EventFullName} name Полное имя события
		 * 
		 * > **ВАЖНО**: Полное имя события может содержать **ТОЛКО** символы англиского алфавита 
		 * в произвольном регистре, цифры и следующие спец. символы: `.`, `+`, `/`, `^` и `:`
		 * 
		 * @see подробнее см. {@link SooperADS.Event.prototype.name SooperADS.Event@name}
		 */
		constructor(target: CustomElementControler, name: EventFullName) {
			super(target, name);

			Object.defineProperty(this, "targetElement", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: this.targetElement
			});
		}
	}
	/**
	 * Событие контролера пользовательского элемента
	 */
	export class CustomElementAttributeEvent<Result = undefined> extends CustomElementEvent<Result> {
		/**
		 * ИМя изменяемого атрибута
		 */
		declare readonly attributeName: string;
		/**
		 * Новое значение атрибута
		 */
		declare readonly attributeNewValue: string | null;
		/**
		 * Старое значение атрибута
		 */
		declare readonly attributeOldValue: string | null;
		/**
		 * Вызвано ли данное событие правкой значения атрибута
		 */
		declare readonly attributeFixed: boolean;

		/**
		 * Создаёт объект SooperADS.Event
		 * @param {CustomElementControler} target Цель события
		 * @param {EventFullName} name Полное имя события
		 * @param {string} attribute Имя атрибута
		 * @param {string | null} oldValue Старое значение атрибута
		 * @param {string | null} newValue Новое значение атрибута
		 * @param {boolean} fix Вызвано ли данное событие правкой значения атрибута
		 * 
		 * > **ВАЖНО**: Полное имя события может содержать **ТОЛКО** символы англиского алфавита 
		 * в произвольном регистре, цифры и следующие спец. символы: `.`, `+`, `/`, `^` и `:`
		 * 
		 * @see подробнее см. {@link SooperADS.Event.prototype.name SooperADS.Event@name}
		 */
		constructor(
			target: CustomElementControler,
			name: EventFullName,
			attribute: string,
			oldValue: string | null,
			newValue: string | null,
			fix: boolean
		) {
			super(target, name);

			Object.defineProperty(this, "attributeName", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: attribute
			});
			Object.defineProperty(this, "attributeOldValue", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: oldValue
			});
			Object.defineProperty(this, "attributeNewValue", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: newValue
			});
			Object.defineProperty(this, "attributeFixed", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: fix
			});
		}
	}

	/**
	 * Карта событий для класса `CustomElementControler`
	 * 
	 * @see {@link SooperADS.CustomElementControler}
	 */
	export type CustomElementEventMap = {
		/**
		 * Класс событий элемента
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"element": CustomElementEvent;
		/**
		 * Событие добавления элемнгта в документ
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"element.attach": CustomElementEvent;
		/**
		 * Событие удаления документа из элемента
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"element.detach": CustomElementEvent;
		/**
		 * Событие перемещения элемента в другой документ
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"element.adopted": CustomElementEvent;
		/**
		 * Класс событий изменения атрибутов
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"element.attribute": CustomElementAttributeEvent;
		/**
		 * Событие создание атрибута
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"element.attribute.create": CustomElementAttributeEvent;
		/**
		 * Событие удаления атрибута
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"element.attribute.delete": CustomElementAttributeEvent;
		/**
		 * Событие изменения атрибута
		 * 
		 * | Критерий               | Значение |
		 * |------------------------|----------|
		 * | Асинхронно             | **НЕТ**  |
		 * | Пасивно                | **ДА**   |
		 * | Поведение по умолчанию | **НЕТ**  |
		 */
		"element.attribute.change": CustomElementAttributeEvent;
	}

	/**
	 * Класс контролера пользовательского элемента
	 * @see {@link SooperADS.CustomElementEventMap}
	 */
	export class CustomElementControler<
		EventOn extends CustomElementEventMap = CustomElementEventMap
	> extends BubbledEventTarget<EventOn & CustomElementEventMap> {
		/**
		 * Владелец контролера
		 */
		declare readonly ownerElement: CustomElement;

		declare readonly parent: CustomElementControler<EventOn> | null;

		/**
		 * Создаёт контролер пользовательского элемента
		 * @param {boolean} activeEventsDispatch Будут ли сейчас обрабвтаться события
		 * @param {CustomElement} ownerElement Элемент, к которому привязан контролер
		 * @param {CustomElementControlerOptions} options Опции создания объекта
		 * @throws Если владелец контролера уже владеет другим контролером — `[TypeError]: Illigal consructor`
		 * @throws Если `ownerElement` не расширяет класс `this.constructor.TARGET_EXTENDS`: `[TypeError]: Incorrect owner element` 
		 */
		constructor(activeEventsDispatch: boolean, ownerElement: CustomElement, options: CustomElementControlerOptions) {
			if (ownerElement.ownerControler) {
				throw new TypeError("Illigal constructor");
			}

			super(activeEventsDispatch, null);

			if (!(ownerElement instanceof (this.constructor as typeof CustomElementControler).TARGET_EXTENDS)) {
				throw new TypeError("Incorrect owner element");
			}

			this.defineEvent("element", true, false, null);
			this.defineEvent("element.adopted", true, false, null);
			this.defineEvent("element.attach", true, false, null);
			this.defineEvent("element.attribute", true, false, null);
			this.defineEvent("element.attribute.change", true, false, null);
			this.defineEvent("element.attribute.create", true, false, null);
			this.defineEvent("element.attribute.delete", true, false, null);

			Object.defineProperty(ownerElement, "ownerControler", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: this
			});
			Object.defineProperty(this, "ownerElement", {
				configurable: false,
				enumerable: true,
				writable: false,
				value: ownerElement
			});

			if (options.parentControler instanceof CustomElementControler) {
				_SET_SLOT(this, Private.CUSTOM_ELEM_OBJECT, "BubbledParent", options.parentControler);
			}
			else {
				this[Private.UpdateParent]();
			}
		}

		/**
		 * Обновляет данные о родителе
		 */
		[Private.UpdateParent](): void {
			let bubbledParent: CustomElementControler | null = null;
			let ft = this.ownerElement.parentElement;
			while (ft) {
				if (ft instanceof CustomElement) {
					bubbledParent = ft.ownerControler;
					break;
				}
				ft = ft.parentElement;
			}

			_SET_SLOT(this, Private.CUSTOM_ELEM_OBJECT, "BubbledParent", bubbledParent);
		}

		/**
		 * Получает контролеры дочерних элементов
		 * @returns {CustomElementControler[]} Список контролеров
		 */
		getChildrens(): CustomElementControler[] {
			let r: CustomElementControler[] = [];

			function __req(elem: Element): void {
				let chl = elem.children;
				for (let i = 0; i < chl.length; i++) {
					const ch = chl.item(i)!;

					if (ch instanceof CustomElement) {
						r.push(ch.ownerControler);
					}
					else {
						__req(ch);
					}
				}
			}

			__req(this.ownerElement);
			return r;
		}

		/**
		 * Класс элемента, поддерживающий данный контролер
		 */
		static declare readonly TARGET_EXTENDS: typeof CustomElement;
	}
	Object.defineProperty(CustomElementControler, "TARGET_EXTENDS", {
		configurable: false,
		enumerable: true,
		writable: false,
		value: CustomElement
	});
	//#endregion

	//#region Инициализация слотов
	_DEFINE_SLOT_TOKEN(Base, "BASE_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(ReadonlyArrayCollection, "READONLY_ARRAY_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(Event, "EVENT_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(BubbledEvent, "BUBBLED_EVENT_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(EventTarget, "EVENT_TARGET_BASE_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(BaseWritable, "BASE_WRITABLE_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(BubbledEventTarget, "BUBBLED_EVENT_TARGET_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(BitColor, "BIT_COLOR_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(Position2D, "POSITION_2D_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(Registry, "REGISTRY_OBJECT", Private);
	_DEFINE_SLOT_TOKEN(CustomElementControler, "CUSTOM_ELEM_OBJECT", Private);

	Private.BASE_OBJECT.define("PrototypeChangeble", "overridable");
	Private.BASE_OBJECT.define("Tag", "always");

	Private.READONLY_ARRAY_OBJECT.define("OwnerArray", "overridable");

	Private.BASE_WRITABLE_OBJECT.define("Readonly", "never");

	Private.BIT_COLOR_OBJECT.define("Binary", "never");
	Private.BIT_COLOR_OBJECT.define("AllowAlpha", "never");

	Private.POSITION_2D_OBJECT.define("X", "overridable");
	Private.POSITION_2D_OBJECT.define("Y", "overridable");

	Private.EVENT_OBJECT.define("EventState", "never");
	Private.EVENT_OBJECT.define("EventLastResult", "overridable");
	Private.EVENT_OBJECT.define("EventAllowDefault", "overridable");
	Private.EVENT_OBJECT.override("PrototypeChangeble", "never");

	Private.EVENT_TARGET_BASE_OBJECT.define("OnEvent", "never");
	Private.EVENT_TARGET_BASE_OBJECT.define("EventExtendable", "never");
	Private.EVENT_TARGET_BASE_OBJECT.define("EventActive", "never");

	Private.REGISTRY_OBJECT.define("Data", "overridable");
	Private.REGISTRY_OBJECT.define("Attributes", "always");

	Private.BUBBLED_EVENT_TARGET_OBJECT.define("BubbledParent", "overridable");

	Private.BUBBLED_EVENT_OBJECT.define("BubbledAllow", "overridable");
	//#endregion
}

//#region Постинициализация
/**
 * @see {@link SooperADS.BitColor}
 */
const BitColor = SooperADS.BitColor;
/**
 * @see {@link SooperADS.BitColor}
 */
type BitColor = SooperADS.BitColor;

/**
 * @see {@link SooperADS.Position2D}
 */
const Position = SooperADS.Position2D;
/**
 * @see {@link SooperADS.Position2D}
 */
type Position = SooperADS.Position2D;

/**
 * @see {@link SooperADS.BitColor.random}
 */
const RandomColor = SooperADS.BitColor.random.bind(SooperADS.BitColor);

/**
 * Генерирует цвет в формате RGBA
 * @param {number} r Красный
 * @param {number} g Зелёный
 * @param {number} b Синий
 * @param {?number} a Непрозрачность
 * @returns {BitColor} Цвет
 */
function Color(r: number, g: number, b: number, a: number = 255): BitColor {
	let c = new BitColor();
	c.set(r, g, b, a);
	return c;
}
/**
 * Генерирует цвет в формате HSVA
 * @param {number} h Оттенок
 * @param {number} s Насыщеность
 * @param {number} v Цвет
 * @param {?number} a Непрозрачность
 * @returns {BitColor} Цвет
 */
function ColorHSV(h: number, s: number, v: number, a: number = 255): BitColor {
	return BitColor.fromHSV({
		hue: h, saturation: s, value: v, alpha: a
	});
}

Object.setPrototypeOf(SooperADS, null);
//#endregion

SooperADS.EventSubNameChars